1 SpringAOP
OOP 👉 面向对象编程 👉 继承
AOP 👉 面向切面编程 👉 切面
要对某一些方法进行增强 👉 按照什么样的方式增强
动态代理:委托类中的全部方法**(动态代理:jdk和cglib 👉 全都要)**
AOP:容器中的组件里的指定的方法
2 AOP编程术语
3 AOP编程实战
3.1 动态代理
cglib(spring阶段可以不引入依赖 👉 spring-aop)
3.2 SpringAOP
3.2.1配置
3.2.1.1 委托类
UserService(接口)
public interface UserService {
public void sayHello(String name);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Override
public void sayHello(String name){
System.out.println("hello"+name);
}
}
3.2.1.2 通知组件
@Component
public class CustomAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//method.invoke(object,args);
// 执行的是委托类的代码
System.out.println("正道的光");
Object proceed = methodInvocation.proceed();// 执行的是委托类的代码
System.out.println("照在大地上");
return proceed;
}
}
3.2.1.3 代理组件
<context:component-scan base-package="aop"/>
<!-- bean definitions here -->
<!--通过bean标签完成组件的注册-->
<!--
id:组件在容器中的唯一标识
name:组件名称,通常省略不写,以id作为name
class:类的全类名
-->
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--委托类组件-->
<property name="target" ref="userServiceImpl"/>
<!--通知-->
<property name="interceptorNames" value="customAdvice"/>
</bean>
3.2.2 单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class MyTest {
@Autowired
@Qualifier("userServiceProxy")
UserService userService;
@Test
public void mytest1(){
userService.sayHello("小雪");
}
}
说明生成的是jdk动态代理
3.3 AspectJ
aspectjweaver
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
3.3.0 全部代码
CustomAspect
/**
* 需要将切面类注册到容器中,作为切面组件
* 通知方法:
*/
@Component
public class CustomAspect {
public void before(JoinPoint joinPoint){ // 👉 before
//通过joinPoint拿到正在执行的方法中的对应的一些值
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println(methodName + "方法携带的参数是:" + Arrays.asList(args));
if (args.length != 0 && "景甜".equals(args[0])){
args[0] = "大甜甜";
}
Object target = joinPoint.getTarget(); //委托类对象
Object aThis = joinPoint.getThis(); //代理对象
System.out.println(target.getClass());
System.out.println(aThis.getClass());
System.out.println("正道的光");
}
public void after(){ // 👉 after
System.out.println("照在大地上");
}
//around通知的返回值为Object 👉 委托类方法的执行结果 👉 类似于invocationHandler的invoke
//参数中包含ProceedingJoinPoint 👉 还要执行委托类的方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("开始时间:" + start);
//执行委托类的方法
Object proceed = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("花费时间为:" + (end - start));
return proceed;
}
public void afterReturning(Object result){
System.out.println("afterReturning:" + result);
}
//Throwable也可以
public void afterThrowing(Exception exception){//需要接收委托类方法执行过程中抛出的异常
System.out.println("afterThrowing:" + exception.getMessage());
}
}
UserService
public interface UserService {
public void sayHello();
public String sayHello2(String name);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
@Override
public void sayHello() {
System.out.println("hello world");
}
@Override
public String sayHello2(String name) {
String result = "hello " + name;
System.out.println(result);
// int i = 1 / 0;
return result;
}
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
<context:component-scan base-package="com.cskaoyan"/>
<aop:config>
<!--service层的任意方法-->
<aop:pointcut id="mypointcut" expression="execution(* com.cskaoyan.service..*(..))"/>
<aop:aspect ref="customAspect">
<!--pointcut和通知的结合
method属性: 方法名,父标签中的ref属性对应的组件中的方法名 👉 切面组件中的方法名
-->
<aop:before method="before" pointcut-ref="mypointcut"/>
<aop:after method="after" pointcut-ref="mypointcut"/>
<aop:around method="around" pointcut-ref="mypointcut"/>
<!--需要通过returning属性指定method对应方法的哪一个参数来接收委托类方法的执行结果-->
<aop:after-returning method="afterReturning" pointcut-ref="mypointcut" returning="result"/>
<!--需要通过throwing属性指定method对应方法的哪一个参数来接收委托类方法执行过程中抛出的异常-->
<aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="exception"/>
</aop:aspect>
</aop:config>
</beans>
MyTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class MyTest {
@Autowired
UserService userService;
@Test
public void mytest1() {
userService.sayHello();
}
@Test
public void mytest2() {
userService.sayHello2("景甜");
}
}
结果:
3.3.1 切入点表达式
指定增强方法
需要使用标签 👉 aop:pointcut 👉 引入aop相关的schema
3.3.1.1 execution(修饰符 返回值 包名、类名、方法名(形参))
群攻
以这样的角度来看:
1、 能否省略不写
2、 能否通配 *
3、 是否包含特殊用法
3.3.1.1.1 修饰符
修饰符:可以省略 👉 省略不写代表任意修饰符
3.3.1.1.2 返回值
返回值:不能省略;可以使用*来通配;如果返回值为pojo,需要写全类名
3.3.1.1.3 包名、类名、方法名
包名、类名、方法名:
可以部分省略 👉 除了头和尾,中间的任意一部分都可以使用…来进行省略
可以通配 👉 *来通配一整个词或词的一部分
3.3.1.1.4 形参
形参:
可以省略 👉 无参方法
特殊用法 👉 和返回值一样,pojo类要写全类名
能否通配 👉 * 代表的单个任意参数;
…任意数量任意类型的参数
3.3.1.2 @annotation(自定义注解的全类名)
精准打击 👉 自定义注解写在容器中的组件的哪个方法上,哪个方法就被增强
3.3.2 advisor(通知器)
都需要提供切入点和通知(如上所示)
更侧重于写自定义的通知
3.3.3 aspect
提供了一些现成的通知 👉 提供了时间信息
通知以方法的形式存在 👉 做了什么样的事情
3.3.3.1 通知
3.3.3.2 新增了afterThrowing
3.3.3.3 JoinPoint连接点
代理对象正在执行的方法
写在哪里呢?
3.3.3.4 注解使用aspectj
切面类 aop:config
aop:config变为注解
CustomAspect
/**
* 需要将切面类注册到容器中,作为切面组件
* 通知方法:
*/
@Aspect
@Component
public class CustomAspect {
//pointcut以方法的形式存在
/**
* 返回值:void
* 方法名:任意写 👉 方法名作为id
* 参数:不需要
* 方法体
* @Pointcut:value属性值写的是切入点表达式
*/
@Pointcut("execution(* com.cskaoyan.service..*(..))")
public void mypointcut() {}
//@Before("execution(* com.cskaoyan.service..*(..))")//相当于直接写切入点表达式
@Before("mypointcut()")
public void before(JoinPoint joinPoint){ // 👉 before
//通过joinPoint拿到正在执行的方法中的对应的一些值
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println(methodName + "方法携带的参数是:" + Arrays.asList(args));
if (args.length != 0 && "景甜".equals(args[0])){
args[0] = "大甜甜";
}
Object target = joinPoint.getTarget(); //委托类对象
Object aThis = joinPoint.getThis(); //代理对象
System.out.println(target.getClass());
System.out.println(aThis.getClass());
System.out.println("正道的光");
}
@After("mypointcut()")
public void after(){ // 👉 after
System.out.println("照在大地上");
}
@Around("mypointcut()")
//around通知的返回值为Object 👉 委托类方法的执行结果 👉 类似于invocationHandler的invoke
//参数中包含ProceedingJoinPoint 👉 还要执行委托类的方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("开始时间:" + start);
//执行委托类的方法
Object proceed = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("花费时间为:" + (end - start));
return proceed;
}
@AfterReturning(value = "mypointcut()",returning = "result")
public void afterReturning(Object result){
System.out.println("afterReturning:" + result);
}
@AfterThrowing(value = "mypointcut()",throwing = "exception")
//Throwable也可以
public void afterThrowing(Exception exception){//需要接收委托类方法执行过程中抛出的异常
System.out.println("afterThrowing:" + exception.getMessage());
}
}
UserService
public interface UserService {
public void sayHello();
public String sayHello2(String name);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
@Override
public void sayHello() {
System.out.println("hello world");
}
@Override
public String sayHello2(String name) {
String result = "hello " + name;
System.out.println(result);
// int i = 1 / 0;
return result;
}
}
application.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
<context:component-scan base-package="com.cskaoyan"/>
<!--注解开关-->
<aop:aspectj-autoproxy/>
</beans>
MyTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class MyTest {
@Autowired
UserService userService;
@Test
public void mytest1() {
userService.sayHello();
}
@Test
public void mytest2() {
userService.sayHello2("景甜");
}
}
结果;
重点注意:
切入点
指定切面组件
@Component不能省略
通知方法的配置