[原创]java WEB学习笔记104:Spring学习---AOP 前奏,通过一个问题引入动态代理
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用
内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系。
本人互联网技术爱好者,互联网技术发烧友
微博:伊直都在0221
QQ:951226918
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题引入
1.实现
原始实现方式:
1 package com.jason.spring.aop.helloworld; 2 3 public interface ArithmeticCaculator { 4 5 int add(int i, int j); 6 int sub(int i, int j); 7 8 int mul(int i, int j); 9 int div(int i, int j); 10 11 }
1 package com.jason.spring.aop.helloworld; 2 3 public class fArithmeticCaculatorloggingImpl implements ArithmeticCaculator { 4 5 @Override 6 public int add(int i, int j) { 7 System.out.println("the method add begins with [" + i + ", " + j + "]"); 8 int result = i + j; 9 System.out.println("the method add end with " + result); 10 return result; 11 } 12 13 @Override 14 public int sub(int i, int j) { 15 System.out.println("the method sub begins with [" + i + ", " + j + "]"); 16 int result = i - j; 17 System.out.println("the method sub end with " + result); 18 return result; 19 } 20 21 @Override 22 public int mul(int i, int j) { 23 System.out.println("the method mul begins with [" + i + ", " + j + "]"); 24 int result = i * j; 25 System.out.println("the method mul end with " + result); 26 return result; 27 } 28 29 @Override 30 public int div(int i, int j) { 31 System.out.println("the method div begins with [" + i + ", " + j + "]"); 32 int result = i / j; 33 System.out.println("the method div end with " + result); 34 return result; 35 } 36 37 }
问题:
1).代码混乱:越来越多的非业务需求(日志和验证等)加入后, 原有的业务方法急剧膨胀. 每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.
2).代码分散: 以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块(方法)里多次重复相同的日志代码. 如果日志需求发生变化, 必须修改所有模块.
解决方式:动态代理
思想:代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.
2. 手动通过动态代理实现(实际开发中不推荐,因为难度较高)
1 package com.jason.spring.aop.helloworld;
2
3 import java.lang.reflect.Method;
4 import java.util.Arrays;
5
6 import org.springframework.cglib.proxy.InvocationHandler;
7 import org.springframework.cglib.proxy.Proxy;
8
9 public class ArithmeticCaculatorLoggingProxy {
10
11 //要代理的对象
12 private ArithmeticCaculator target;
13
14 public ArithmeticCaculatorLoggingProxy(ArithmeticCaculator target) {
15 this.target = target;
16 }
17
18
19
20 public ArithmeticCaculator getLoggingProxy(){
21 ArithmeticCaculator proxy = null;
22
23 //代理对象由哪一个类加载器负责加载
24 ClassLoader loader = target.getClass().getClassLoader();
25
26 //代理对象的类型,即其中有那些方法
27 Class[] interfaces = new Class[]{ArithmeticCaculator.class};
28
29 //当调用代理对象,其中的方法时,该执行的代码
30 InvocationHandler invocationHandler = new InvocationHandler() {
31 /**
32 * proxy:正在返回的那个代理对象,一般情况下,在invoke 方法中都不使用对象
33 * method:正在被调用的方法
34 * args:调用方法时,传入的参数
35 */
36 @Override
37 public Object invoke(Object proxy, Method method, Object[] args)
38 throws Throwable {
39 String methodName = method.getName();
40
41 //日志
42 System.out.println("The method " + methodName + "begins with " + Arrays.asList(args));
43 //执行方法
44 Object result = method.invoke(target, args);
45 //日志
46 System.out.println("The method " + methodName + "end with " + result);
47 return result;
48 }
49 };
50
51 proxy =(ArithmeticCaculator) Proxy.newProxyInstance(loader, interfaces, invocationHandler);
52
53
54 return proxy;
55 }
56
57 }
测试
Main.java
1 package com.jason.spring.aop.helloworld;
2
3 public class Main {
4
5 public static void main(String[] args) {
6 /*ArithmeticCaculator arithmeticCaculator = null;
7 arithmeticCaculator = new ArithmeticCaculatorImpl();*/
8
9 ArithmeticCaculator target = new ArithmeticCaculatorImpl();
10 ArithmeticCaculator proxy = new ArithmeticCaculatorLoggingProxy(target).getLoggingProxy();
11
12
13 int result = proxy.add(1, 2);
14 System.out.println("--> " + result);
15
16 result = proxy.sub(1, 2);
17 System.out.println("--> " + result);
18 }
19
20 }
3.简便方式:使用springAOP 原理 见下一节 105
1 package com.jason.spring.aop.impl; 2 3 public interface ArithmeticCaculator { 4 5 int add(int i, int j); 6 int sub(int i, int j); 7 8 int mul(int i, int j); 9 int div(int i, int j); 10 11 }
1 package com.jason.spring.aop.impl; 2 3 import org.springframework.stereotype.Component; 4 5 6 @Component 7 public class ArithmeticCaculatorImpl implements ArithmeticCaculator { 8 9 @Override 10 public int add(int i, int j) { 11 int result = i + j; 12 return result; 13 } 14 15 @Override 16 public int sub(int i, int j) { 17 int result = i - j; 18 return result; 19 } 20 21 @Override 22 public int mul(int i, int j) { 23 int result = i * j; 24 return result; 25 } 26 27 @Override 28 public int div(int i, int j) { 29 int result = i / j; 30 return result; 31 } 32 33 }
1 package com.jason.spring.aop.impl; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.annotation.After; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Before; 10 import org.springframework.stereotype.Component; 11 12 13 //把这个类声明为一个切面 14 //1.需要将该类放入到IOC 容器中 15 @Component 16 //2.再声明为一个切面 17 @Aspect 18 public class LoggingAspect { 19 20 //声明该方法是一个前置通知:在目标方法开始之前执行 哪些类,哪些方法 21 //作用:@before 当调用目标方法,而目标方法与注解声明的方法相匹配的时候,aop框架会自动的为那个方法所在的类生成一个代理对象,在目标方法执行之前,执行注解的方法 22 //支持通配符 23 //@Before("execution(public int com.jason.spring.aop.impl.ArithmeticCaculatorImpl.*(int, int))") 24 @Before("execution(* com.jason.spring.aop.impl.*.*(int, int))") 25 public void beforeMethod(JoinPoint joinPoint){ 26 String methodName = joinPoint.getSignature().getName(); 27 List<Object> args = Arrays.asList(joinPoint.getArgs()); 28 System.out.println("The method " + methodName + " begins " + args); 29 } 30 31 @After("execution(* com.jason.spring.aop.impl.*.*(int, int))") 32 public void fterMethod(JoinPoint joinPoint){ 33 String methodName = joinPoint.getSignature().getName(); 34 List<Object> args = Arrays.asList(joinPoint.getArgs()); 35 System.out.println("The method " + methodName + " end " + args); 36 } 37 38 39 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 9 10 11 <!-- 配置自动扫描的包 --> 12 <context:component-scan base-package="com.jason.spring.aop.impl"></context:component-scan> 13 14 <!-- 使 AspjectJ 注解作用:自动为匹配的类生成代理对象 --> 15 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 16 17 </beans>
1 package com.jason.spring.aop.impl; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class Main { 7 8 public static void main(String[] args) { 9 10 //1.创建Spring 的IOC 容器 11 ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); 12 13 //2.从IOC 容器中获取 bean实例 14 ArithmeticCaculator arithmeticCaculator = (ArithmeticCaculator) ctx.getBean(ArithmeticCaculator.class); 15 16 //3.使用bean 17 int result = arithmeticCaculator.add(1, 2); 18 System.out.println(result); 19 20 result = arithmeticCaculator.div(1, 2); 21 System.out.println(result); 22 23 24 } 25 26 }