AOP
对于代理模式而言,所创建的代理类是去代理执行和调用目标类的方法,而不是去重新实现目标类方法,它只是对目标类的一种增强。虽然在技术实现上代理类是通过继承目标类或实现目标类接口来创建的代理类,但是在实际调用过程当中,对于目标方法的最终执行依旧是所传入的目标对象
静态代理
代理模式实际上是通过调用代理的相关方法,再由代理去调用实际对象的方法
可以把代理模式看作是对实际对象的一种封装,由代理对象对实际对象进行一些功能上的增强(如添加日志)
根据这种设计思路,那么在设计代理类时,就需要将目标类的实例对象传递到代理类的实例中,通过调用代理实例的方法来间接调用目标类的方法。从而实现代理的功能
目标类的接口
public interface Calculator {
public int add(int a, int b);
public int subtract(int a, int b);
public int multiply(int a, int b);
public int divide(int a, int b);
}
目标类
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public int subtract(int a, int b) {
return a-b;
}
@Override
public int multiply(int a, int b) {
return a*b;
}
@Override
public int divide(int a, int b) {
return a/b;
}
}
代理类
public class CalculatorHandler implements Calculator {
private Calculator calculator;
public CalculatorHandler(CalculatorImpl calculator) {
this.calculator = calculator;
}
@Override
public int add(int a, int b) {
System.out.println(a+"+"+b+"="+calculator.add(a,b));
return calculator.add(a, b);
}
@Override
public int subtract(int a, int b) {
System.out.println(a+" -"+b+"="+calculator.subtract(a,b));
return calculator.subtract(a, b);
}
@Override
public int multiply(int a, int b) {
System.out.println(a+" *"+b+"="+calculator.multiply(a,b));
return calculator.multiply(a, b);
}
@Override
public int divide(int a, int b) {
System.out.println(a+" /"+b+"="+calculator.divide(a,b));
return calculator.divide(a, b);
}
}
测试类
public class CalculatorProxyTest {
@Test
public void testCalculatorProxy() {
CalculatorImpl calculatorImpl = new CalculatorImpl();
Calculator calculatorHandler = new CalculatorHandler(calculatorImpl);
calculatorHandler.add(1,2);
calculatorHandler.subtract(1,2);
calculatorHandler.multiply(1,2);
calculatorHandler.divide(1,2);
}
}
动态代理
jdk动态代理
JDK动态代理实现的基本逻辑是实现目标类接口,然后利用Java的反射机制,在运行时动态生成代理类,通过代理类调用被代理对象的方法,并在invoke方法中添加额外的逻辑
需要代理类的接口
public interface UserService {
public void work();
}
需要代理的类
public class UserServiceImpl implements UserService {
@Override
public void work() {
System.out.print("i am work ");
}
}
代理处理器
public class UserHandler implements InvocationHandler {
//将要代理的实体类对象
private Object target;
public UserHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("this is company ");
Object result = method.invoke(target, args);
LocalDate currentDate = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String formattedDate = currentDate.format(formatter);
System.out.println(formattedDate);
return result;
}
}
测试
public class UserHandlerTest {
@Test
public void test() {
//创建需要代理的类的实例
UserService userServiceImpl = new UserServiceImpl();
//创建代理处理器的实例
UserHandler userHandler = new UserHandler(userServiceImpl);
//创建运行时代理类
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userServiceImpl.getClass().getClassLoader(),
userServiceImpl.getClass().getInterfaces(),
userHandler
);
userServiceProxy.work();
}
}
- 创建代理类:当调用
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
方法时,JDK会在运行时动态生成一个代理类,这个代理类会实现所指定的接口,并在方法调用时,将调用委托给InvocationHandler
的实现对象 - 生成字节码:在运行时,JDK使用
ProxyGenerator.generateProxyClass
方法生成代理类的字节码。这个字节码包含了代理类的定义,以及方法调用 - 加载代理类:生成的字节码被加载到内存中,通过反射创建代理类实例
- 调用代理方法:当调用代理对象的方法时,实际上是调用了代理类中的方法。代理类中的方法将方法调用委托給
InvocationHandler
的invoke
方法。 - 调用处理器的
invoke
方法:在InvocationHandler
的invoke
方法中,可以定义代理对象方法的行为。该方法接收三个参数:代理对象本身,被调用的方法对象和方法的参数数组。在invok
方法中,可以对方法进行增强、修改或者添加额外的逻辑 - 执行原始方法:在invoke方法中,通常会调用
Method
对象的invoke
方法来执行原始对象中对应的方法 - 返回结果:
invoke
方法返回的结果会称为代理对象方法的返回值
cglib动态代理
cglib的动态代理是在生成代理类时创建目标类的子类,并在子类中对目标方法进行拦截,在方法执行前后插入自定义逻辑实现的
目标类
public class Coder {
public void work() {
System.out.println("认真写bug……");
}
}
代理类
public class AttendanceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("上班打卡……");
Object result = proxy.invokeSuper(obj, args);
System.out.println("下班打卡……");
return result;
}
}
测试类
public class CglibProxyTest {
@Test
public void Test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Coder.class);
enhancer.setCallback(new AttendanceMethodInterceptor());
// 创建代理对象
Object object = enhancer.create();
Coder coder = (Coder) object;
coder.work();
}
}
AspectJ
是AOP思想的一种实现,本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。spring只是借用了AspectJ中的注解
基于注解的AOP
快速入门
配置文件
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
基于注解的aop的实现
1.将目标对象和切面自动交给IOC容器管理(扫描+注解)
2.开启AspectJ的自动代理,为目标对象自动生成代理
3.将切面类通过注解@Aspect标识
-->
<context:component-scan base-package="com.atli.springaop"></context:component-scan>
<!-- 开启aspectj自动代理,为目标对象生成代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
目标类接口
public interface Calculator {
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
}
目标类实现类
@Component
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println("this is add function running ");
return a+b;
}
@Override
public int sub(int a, int b) {
System.out.println("this is sub function running: ");
return a-b;
}
@Override
public int mul(int a, int b) {
System.out.println("this is mul function running: ");
return a*b;
}
@Override
public int div(int a, int b) {
System.out.println("this is div function running: ");
return a/b;
}
}
切面类
@Component//装入ioc
@Aspect//切面类
public class LogAspect {
//设置切入点和通知类型
//切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类全路径.方法名称(方法参数))
//重用切点表达式
@Pointcut(value = "execution(public int com.atli.springaop.CalculatorImpl.*(int,int))")
public void pointCut() {}
// 前置:@Before()
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("this is @Before " + joinPoint.getSignature().getName());
}
// 返回:@AfterReturning
//JoinPoint获取方法名,参数等信息
//result获取方法返回值信息
@AfterReturning(value = "pointCut()"。returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
System.out.println("this is @AfterReturning " + joinPoint.getSignature().getName());
}
// 异常:@AfterThrowing
// 只有在抛出异常时才执行
@AfterThrowing(value="pointCut()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("this is @AfterThrowing " + joinPoint.getSignature().getName());
}
// 后置:@After()
@After(value = "pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("this is @After " + joinPoint.getSignature().getName());
}
// // 环绕:@Around()
// // 注意,环绕通知和其他通知不能同时使用,
// @Around(value = "pointCut()")
// public void Around(JoinPoint joinPoint){
// System.out.println("this is @Around " + joinPoint.getSignature().getName());
// }
}
测试类
public class AopTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Calculator calculatorImpl = (Calculator) context.getBean("calculatorImpl");
int a = calculatorImpl.add(1, 2);
System.out.println("this is @Test: "+a);
}
}
相关术语
切面(Aspect):切面是横切关注点的模块化单元,它封装了与横切关注点相关的行为和逻辑。切面定义了在何处以及何时应用横切关注点。在 Spring AOP 中,切面通常是一个类,其中包含了一系列的通知(Advice)和切点(Pointcut)。
通知(Advice):通知定义了在切面的何处以及何时执行横切关注点的逻辑。在 AOP 中,有几种类型的通知,包括前置通知(Before advice)、后置通知(After advice)、返回通知(After-returning advice)、异常通知(After-throwing advice)和环绕通知(Around advice)。每种类型的通知在不同的时机执行横切关注点的逻辑。
切点(Pointcut):切点定义了在哪些连接点(Join Point)上应用横切关注点。连接点是程序执行过程中的特定位置,比如方法调用或异常抛出等。切点通过使用表达式或模式匹配规则来确定连接点的集合。
连接器(Join Point):连接器是程序执行过程中的特定位置,它是切点的实际实例。在 AOP 中,连接器表示方法调用、异常抛出等具体的执行点。
引入(Introduction):引入允许在现有的类中添加新的方法和属性。通过引入,我们可以在不修改原有代码的情况下,向现有类添加新的功能。
织入(Weaving):织入是将切面应用到目标对象中的过程。织入可以在编译时、类加载时或运行时进行。Spring AOP 使用运行时织入,它通过创建代理对象来将切面织入到目标对象中。