文章目录
一、动态代理
1.1 动态代理
- invoke中参数是具体的被代理对象
public class test {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
List list = (List) Proxy.newProxyInstance(arrayList.getClass().getClassLoader(), arrayList.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("向数组中添加了元素");
return method.invoke(arrayList, args);
}
return method.invoke(proxy, args);
}
});
list.add(1);
}
}
1.2 cglib代理
- 要求:被代理的类不能是最终类
public class test {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
ArrayList arrayList1 = (ArrayList) Enhancer.create(arrayList.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("使用了增加的方法");
return method.invoke(arrayList, objects);
}
return method.invoke(arrayList, objects);
}
});
arrayList1.add(1);
}
}
1.3 动态代理的好处
- 可以把一些重复代码写进动态代理中,提高编程的效率。
1.4 Aop作用
- 程序运行期间,不修改源码,对已有代码进行增强。
减少重复代码
提高开发效率
维护方便
1.5 aop实现方式
- 使用动态代理技术
二、spring中的aop
2.1 aop相关术语
- joinpoint(连接点)
指方法 - pointcut(切入点)
指对哪些连接点进行拦截,一般指被增强的连接点。 - 通知
拦截到连接点之后要做的事情
类型:前置通知,后置通知,环绕通知,异常通知,返回通知。
catch里面一般是异常通知,finally里面一般是最终通知
-
weaving(织入)
增强应用到目标对象创建新的代理对象的过程。 -
切面
切入点和通知的结合
2.2 spring框架的作用
spring框架监控切入点的执行,一旦监控到切入点执行,就会使用动态代理,创建目标对象的代理对象,根据通知的类型,在代理对象的对应位置,将通知织入,完成代码的逻辑运行。
2.3 切入点表达式
访问修饰符 返回值 包名.包名…类名.方法名(参数列表)
2.4 基于xml方式配置aop
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<!-- 被增强的类-->
<bean id="stuservice" class="com.example.demo.Service.stuService"></bean>
<!-- 增强所在的类-->
<bean id="countlog" class="com.example.demo.Logger.CountLog"></bean>
<aop:config>
<aop:aspect id="logService" ref="countlog">
<!--配置通知的类型, 并且建立通知方法和切入点的关联-->
<aop:before method="log" pointcut="execution(public int com.example.demo.Service.stuService.getCount())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
2.5 测试aop
- 需要倒入相关的依赖
这个包的主要作用是解析切入点表达式
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 测试
class stuServiceTest {
@Test
void getCount() {
//1.获取容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象
stuService stuservice = (stuService) ac.getBean("student");
//3.执行方法
stuservice.getCount();
}
}
- 结果
在取款前打印日志
获取剩余的存款
2.6 切入点表达式
- 全通配写法
* *..*.*()
可以用一个*号匹配一个包名,用…表示所有的包
- 可以写参数类型
基本数据类型直接写类型名称
引用数据类型写包名.类型名
2.7 其他通知类型测试
- bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<!-- 被增强的类-->
<bean id="student" class="com.example.demo.Service.stuService"></bean>
<!-- 增强所在的类-->
<bean id="countLog" class="com.example.demo.Logger.CountLog"></bean>
<aop:config>
<aop:aspect id="logService" ref="countLog">
<!--配置通知的类型, 并且建立通知方法和切入点的关联-->
<aop:before method="beforeAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:before>
<aop:after method="afterAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:after>
<aop:after-returning method="returnAdvice" pointcut="execution(public int com.example.demo.Service.stuService.getCount())"></aop:after-returning>
<aop:around method="SurroundAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:around>
<aop:after-throwing method="WrongAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
- 结果
前置通知
环绕通知
后置通知
null
2.8 报错
org.springframework.aop.AopInvocationException: Null return value from advice does not match primiti
将基本数据类型改成引用数据类型
2.9 通知总结
- 前置通知在切入点执行前执行
- 后置通知在切入点执行后执行
- 最终通知是无论切入点是否正常执行都会执行
- 异常通知是发生异常后执行
2.10 通知执行顺序
befrore–>around–>after–>after-throwing/after-return
2.11 配置切入点表达式
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<!-- 被增强的类-->
<bean id="student" class="com.example.demo.Service.stuService"></bean>
<!-- 增强所在的类-->
<bean id="countLog" class="com.example.demo.Logger.CountLog"></bean>
<aop:config>
<aop:aspect id="logService" ref="countLog">
<!--配置通知的类型, 并且建立通知方法和切入点的关联-->
<aop:before method="beforeAdvice" pointcut-ref="express1"></aop:before>
<aop:after method="afterAdvice" pointcut-ref="express1"></aop:after>
<aop:after-returning method="returnAdvice" pointcut-ref="express1"></aop:after-returning>
<aop:after-throwing method="WrongAdvice" pointcut-ref="express1"></aop:after-throwing>
<aop:around method="SurroundAdvice" pointcut-ref="express1"></aop:around>
<aop:pointcut id="express1" expression="execution(public * com.example.demo.Service.stuService.getCount())"/>
</aop:aspect>
</aop:config>
</beans>
2.12 环绕通知
public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
Integer proceed = 0;
try {
System.out.println("around start");
proceed = (Integer)joinPoint.proceed(args);
System.out.println("around after");
} catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
return proceed;
}
}
- 测试最终结果
before
around start
around after
after-return
after
三、注解方式实现aop
3.1 配置文件的修改
在配置文件中加入允许注解相关配置
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解需要扫描的包-->
<context:component-scan base-package="com.example.demo"></context:component-scan>
<!-- 开启支持aop相关的注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
3.2 切面类
package com.example.demo.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component("logger")
@Aspect
public class CountLog {
@Pointcut("execution(public * com.example.demo.Service.stuService.getCount())")
public void p1(){}
@Before("p1()")
public void beforeAdvice(){
System.out.println("before");
}
@After("p1()")
public void afterAdvice(){
System.out.println("after");
}
@AfterThrowing("p1()")
public void WrongAdvice(){
System.out.println("wrong");
}
/*@Around("p1()")
public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
Integer proceed = 0;
try {
System.out.println("around start");
proceed = (Integer)joinPoint.proceed(args);
System.out.println("around after");
} catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
return proceed;
}
}*/
@AfterReturning("p1()")
public void returnAdvice(){
System.out.println("after-return");
}
}
3.3 结果
before
after
after-return
800
3.4 不使用xml配置的方式
- 配置类
package com.example.demo.Service.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.example.demo")
@EnableAspectJAutoProxy
public class aopCofig {
}
- 测试类
class stuServiceTest {
@Test
void getCount() {
//1.获取容器
ApplicationContext ac = new AnnotationConfigApplicationContext("com.example.demo.Service.Configuration");
//2.获取对象
stuService stuservice = (stuService) ac.getBean("student");
//3.执行方法
System.out.println(stuservice.getCount());
}
}
3.5 各种通知类型模拟
@Around("p1()")
public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
Integer proceed = 0;
try {
System.out.println("前置通知");
proceed = (Integer)joinPoint.proceed(args);
System.out.println("后置通知");
System.out.println("around after");
} catch (Throwable throwable) {
System.out.println("异常通知");
throwable.printStackTrace();
}finally {
System.out.println("after通知");
return proceed;
}
}