现在有一个接口Calculator
1 2 3 4 5 6
| 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); }
|
其实现类CalculatorImpl实现了基本的算数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { return a + b; }
@Override public int sub(int a, int b) { return a - b; }
@Override public int mul(int a, int b) { return a * b; }
@Override public int div(int a, int b) { return a / b; } }
|
那么现在有一个需求, 就是我需要在每次计算前后打印日志, 显示参数a和b是多少, 以及计算结果是多少
重写实现类
使用CalculatorLogImpl重新去实现Calculator接口, 便可达到目的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class CalculatorLogImpl implements Calculator { @Override public int add(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = a + b; System.out.println("[日志]结果为: " + result); return result; }
@Override public int sub(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = a - b; System.out.println("[日志]结果为: " + result); return result; }
@Override public int mul(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = a * b; System.out.println("[日志]结果为: " + result); return result; }
@Override public int div(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = a / b; System.out.println("[日志]结果为: " + result); return result; } }
|
测试一下
1 2 3 4 5 6 7 8
| @Test public void testCalculatorLog() { Calculator calculator = new CalculatorLogImpl(); calculator.add(1, 2); calculator.sub(1, 2); calculator.mul(1, 2); calculator.div(1, 2); }
|
没有问题
1 2 3 4 5 6 7 8 9 10
| [日志]参数为: 1, 2 [日志]结果为: 3 [日志]参数为: 1, 2 [日志]结果为: -1 [日志]参数为: 1, 2 [日志]结果为: 2 [日志]参数为: 1, 2 [日志]结果为: 0
进程已结束,退出代码0
|
但是很明显, 方法中新增了与业务逻辑无关的代码, 且这样冗余代码过多, 每个方法中都有相同的代码
静态代理
使用静态代理类CalculatorStaticProxy优化(封装), 业务代码一层, 日志再封装一层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class CalculatorStaticProxy implements Calculator{ private Calculator calculator;
public CalculatorStaticProxy(Calculator calculator) { this.calculator = calculator; }
@Override public int add(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = calculator.add(a, b); System.out.println("[日志]结果为: " + result); return result; }
@Override public int sub(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = calculator.sub(a, b); System.out.println("[日志]结果为: " + result); return result; }
@Override public int mul(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = calculator.mul(a, b); System.out.println("[日志]结果为: " + result); return result; }
@Override public int div(int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = calculator.div(a, b); System.out.println("[日志]结果为: " + result); return result; } }
|
测试没问题, 虽然这样分离了业务逻辑代码和日志, 但是代码依然冗余
(Attention: 这里使用的是CalculatorImpl接口实现类, 之后也都使用的这这个, 而不是CalculatorLogImpl)
1 2 3 4 5 6 7 8
| @Test public void testStaticProxy() { CalculatorStaticProxy calculatorStaticProxy = new CalculatorStaticProxy(new CalculatorImpl()); calculatorStaticProxy.add(1, 2); calculatorStaticProxy.sub(1, 2); calculatorStaticProxy.mul(1, 2); calculatorStaticProxy.div(1, 2); }
|
动态代理
使用动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class DynamicProxy {
public static Object getProxy(Object target) { ClassLoader classLoader = target.getClass().getClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); InvocationHandler invocationHandler = (proxy, method, args) -> { int a = (int) args[0], b = (int) args[1]; System.out.println("[日志]参数为: " + a + ", " + b); Object result = method.invoke(target, args); System.out.println("[日志]结果为: " + result); return result; }; return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } }
|
代码一下就简洁了很多, 优雅!
1 2 3 4 5 6 7 8
| @Test public void testCalculatorDynamicProxy() { Calculator proxy = (Calculator) DynamicProxy.getProxy(new CalculatorImpl()); proxy.add(1, 2); proxy.sub(1, 2); proxy.mul(1, 2); proxy.div(1, 2); }
|
函数式编程
还有种方式, 函数式编程也可实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.util.function.IntBinaryOperator;
public class FunctionalCode {
public static int calculateLog(IntBinaryOperator calculator, int a, int b) { System.out.println("[日志]参数为: " + a + ", " + b); int result = calculator.applyAsInt(a, b); System.out.println("[日志]结果为: " + result); return result; } }
|
测试, 需传入具体使用的方法, 将方法参数化
1 2 3 4 5 6 7 8
| @Test public void testFunctional() { Calculator calculator = new CalculatorImpl(); FunctionalCode.calculateLog(calculator::add, 1, 2); FunctionalCode.calculateLog(calculator::sub, 1, 2); FunctionalCode.calculateLog(calculator::mul, 1, 2); FunctionalCode.calculateLog(calculator::div, 1, 2); }
|
Spring的Aspects
Spring的AOP其实也是使用的动态代理的方式实现
需导入依赖aspects, aop在context包中带有
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency>
|
日志切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package org.example.aop;
@Aspect @Component public class LogAspect {
@Pointcut("execution(public int org.example.Calculator.*(..))") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); int a = (int) args[0], b = (int) args[1]; System.out.println("[日志]参数为: " + a + ", " + b); int result = (int) pjp.proceed(); System.out.println("[日志]结果为: " + result); return result; } }
|
配置类
1 2 3 4 5 6 7 8 9 10
| @ComponentScan("org.example.aop") @Configuration @EnableAspectJAutoProxy public class AppConfig { @Bean public Calculator calculator(){ return new CalculatorImpl(); } }
|
测试
1 2 3 4 5 6 7 8 9
| @Test public void testSpringAspect(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); Calculator calculator = ctx.getBean("calculator", Calculator.class); calculator.add(1, 2); calculator.sub(1, 2); calculator.mul(1, 2); calculator.div(1, 2); }
|
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @SpringJUnitConfig(classes = AppConfig.class)
public class SpringTest { @Autowired private Calculator calculator;
@Test public void testSpringAspect() { calculator.add(1, 2); calculator.sub(1, 2); calculator.mul(1, 2); calculator.div(1, 2); } }
|
可以看出, 一种需求的实现方式是有很多种的, 根据实际情况选择更适合的