Spring—AOP编程
笔记整理自 b站 孙哥说Spring5

1. 静态代理设计模式
Ⅰ. 为什么需要代理设计模式
问题
-
在JavaEE分层开发开发中,哪个层次对于我们来讲最重要
DAO ---> Service --> ControllerJavaEE分层开发中,最为重要的是Service层
-
Service层中包含了哪些代码?
Service层中 = 核心功能(几十行 上百代码) + 额外功能(附加功能) 1. 核心功能 业务运算 DAO调用 2. 额外功能 1. 不属于业务 2. 可有可无 3. 代码量很小 事务、日志、性能...


-
额外功能书写在Service层中好不好?
Service层的调用者的角度(Controller):需要在Service层书写额外功能。 软件设计者:Service层不需要额外功能。 -
现实生活中的解决方式
租房场景
租房场景改造既然房东不想做额外功能,但房客还需要这些功能,所以此时需要有一个第三方:中介 负责这些功能,在程序中也就是我们的代理类
Proxy
Ⅱ. 代理设计模式
1️⃣ 概念
- 通过代理类,为原始类(目标)增加额外的功能。
- 好处:利于原始类(目标)的维护。
2️⃣ 名词解释
-
原始(目标)类
➢ 指的是 业务类(核心功能 => 业务运算 DAO调用)
-
原始(目标)方法
➢ 原始(目标)类中的方法 就是原始(目标)方法
-
额外功能(附加功能)
➢ 日志、事务、性能
3️⃣ 代理开发的核心要素
代理类 = 目标类(原始类) + 额外功能 + 原始类(目标类)实现相同的接口
要求: 代理类的方法名与目标类的方法名要一一对应(即代理类要和目标类实现同一个接口,这也就是为什么我们的service层一定要写一个接口然后写实现类的原因!!!)
房东 => public interface UserService{
m1
m2
}
UserServiceImpl implements UserService {
m1 => 业务运算 DAO调用
m2
}
UserServiceProxy implements UserService {
m1
m2
}
4️⃣ 编码
- 静态代理:为每一个原始类,手工编写一个代理类 (.java .class)

5️⃣ 静态代理存在的问题
-
静态类文件数量过多,不利于项目管理
➢
UserServiceImpl => UserServiceProxy➢
OrderServiceImpl => OrderServiceProxy -
额外功能维护性差
➢ 代理类中 额外功能修改复杂(麻烦)需要修改所有的代理类
基于上面的两个致命缺点,我们可以使用动态代理,解决了上面的两个缺点。
动态代理没有代理类,使用字节码技术生成代理类额外功能单独抽取,所有需要此额外功能的目标类都可以使用,所以修改额外功能非常方便
2. Spring的动态代理开发

Ⅰ. Spring动态代理的概念
- 概念:通过代理类为原始类(目标类)增加额外功能。
- 好处:利于原始类(目标类)的维护。
Ⅱ. 搭建开发环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
Ⅲ. Spring动态代理的开发步骤
1️⃣ 创建原始对象(目标对象)
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
配置文件applicationContext.xml
<bean id="userService" class="com.lyc.proxy.UserServiceImpl"/>
2️⃣ 额外功能
MethodBeforeAdvice接口
额外的功能书写在接口的实现中,运行在原始方法执行之前运行额外功能。
public class Before implements MethodBeforeAdvice {
/*
作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----method before advice log------");
}
}
配置文件applicationContext.xml
<bean id="before" class="com.lyc.dynamic.Before"/>
3️⃣ 定义切入点
-
切入点:额外功能加入的位置。
-
目的:由程序员根据自己的需要,决定额外功能加入给哪个原始方法。
-
简单的测试:所有方法都做为切入点,都加入额外的功能。
<aop:config> <!-- aop:pointcut :切入点 expression :切入点表达式 execution(* *(..)) :代表所有方法都作为切入点加入额外功能 login、register --> <aop:pointcut id="pc" expression="execution(* *(..))"/> </aop:config>
4️⃣ 组装
-
将 2 2 2、 3 3 3 步整合,嵌套在
<aop:config>标签中,组装成切面<!-- 组装:目的把切入点 与 额外功能 进行整合 表达的含义:所有的方法 都加入 before的额外功能 --> <aop:advisor advice-ref="before" pointcut-ref="pc"/>
5️⃣ 调用
-
目的:获得Spring工厂创建的动态代理对象,并进行调用。
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); /* 注意: 1. Spring的工厂通过原始对象的id值获得的是代理对象 2. 获得代理对象后,可以通过声明接口类型,进行对象的存储 */ UserService userService = (UserService) ctx.getBean("userService"); userService.login(); userService.register();达到效果

Debug调试一下,通过原始对象id获取的对象的确是代理对象
Proxy
Ⅳ. 动态代理细节分析
-
Spring创建的动态代理类在哪里?
➢ Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失。
➢ 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。
➢ 结论:动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理 类文件数量过多,影响项目管理的问题。

-
动态代理编程简化代理的开发
➢ 在额外功能不改变的前提下,创建其他原始类(目标类)的代理对象时,只需指定原始(目标)对象即可。
-
动态代理额外功能的维护性大大增强
3. Spring动态代理详解
Ⅰ. 额外功能的详解
-
MethodBeforeAdvice分析(使用较少)方法中的所有参数都是基于原始类
动态变化的// 1.MethodBeforeAdvice接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。 public class Before implements MethodBeforeAdvice { /** * 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中 * param1 Method method :额外功能所增加给的原始方法(login、register) * param2 Object[] objects :额外功能所增加给的那个原始方法的参数((String name, String password)、(User user)) * param3 Object o :额外功能所加给的原始对象(UserServiceImpl、OrderServiceImpl) * 所有参数都是基于原始类动态变化的 */ @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("-----method before advice log------"); } }// 2.before方法的3个参数在实战中,该如何使用。 // before方法的参数,在实战中,会根据需要进行使用,不一定都会用到,也有可能都不用。 Servlet{ service(HttpRequest request, HttpResponse response) { request.getParameter("name") --> response.getWriter() ---> } } -
MethodInterceptor(方法拦截器)(使用较多,因为功能强大)// MethodInterceptor接口:额外功能可以根据需要运行在原始方法执行:前、后、前后。 public class Arround implements MethodInterceptor { /** * invoke()方法作用:额外功能书写在invoke方法中 * 确定:原始方法怎么运行 * @param methodInvocation :额外功能所增加给的那个原始方法(login、register) * methodInvocation.proceed() => (login、register)原始方法运行 * @return Object ret:原始方法的返回值 */ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { // TODO 原始方法执行之前书写额外功能 Object ret = methodInvocation.proceed(); // TODO 原始方法执行之后书写额外功能 return ret; } }➢ 额外功能运行在原始方法执行之前
@Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("=======原始方法执行之前的额外功能======="); Object ret = methodInvocation.proceed(); return ret; }➢ 额外功能运行在原始方法执行之前
@Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { Object ret = methodInvocation.proceed(); System.out.println("=======原始方法执行之后的额外功能======="); return ret; }➢ 额外功能运行在原始方法执行之前、之后
// 什么样的额外功能 运行在原始方法执行之前,之后都要添加? // 事务 @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("=======原始方法执行之前的额外功能======="); Object ret = methodInvocation.proceed(); System.out.println("=======原始方法执行之后的额外功能======="); return ret; }
➢ 额外功能运行在原始方法抛出异常的时候
@Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { Object ret = null; try { ret = methodInvocation.proceed(); } catch (Throwable throwable) { System.out.println("=======原始方法抛出异常 执行的额外功能======="); throwable.printStackTrace(); } return ret; }➢ MethodInterceptor影响原始方法的返回值
// 原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值 // MethodInterceptor影响原始方法的返回值:Invoke方法的返回值,不要直接返回原始方法的运行结果即可。 @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("-----log-----"); Object ret = invocation.proceed(); return false; }
Ⅱ. 切入点详解
-
切入点决定额外功能加入位置(方法)
<aop:pointcut id="pc" expression="execution(* *(..))"/> exection(* *(..)) => 匹配了所有方法 a× b√ c× ? 1. execution() 切入点函数 2. * *(..) 切入点表达式
1️⃣ 切入点表达式
-
方法切入点表达式

* *(..) => 所有方法 * => 修饰符 返回值 * => 方法名 () => 参数表 .. => 对于参数没有要求(参数有没有都行,参数有几个都行,参数是什么类型的都行)➢ 定义
login方法作为切入点* login(..) # 定义register作为切入点 * register(..)➢ 定义login方法且login方法有
两个字符串类型的参数作为切入点* login(String,String) # 注意:非java.lang包中的类型,必须要写全限定名 * register(com.lyc.proxy.User) # ..可以和具体的参数类型连用 * login(String,..) => login(String),login(String,String),login(String,com.lyc.proxy.User)➢ 精准方法切入点限定


修饰符 返回值 包.类.方法(参数) * com.lyc.proxy.UserServiceImpl.login(..) * com.lyc.proxy.UserServiceImpl.login(String,String) -
类切入点表达式
指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能。

➢ 语法1
# 类中的所有方法加入了额外功能 * com.lyc.proxy.UserServiceImpl.*(..)➢ 语法2
# 忽略包 1. 类只存在一级包 com.UserServiceImpl * *.UserServiceImpl.*(..) 2. 类存在多级包 com.lyc.proxy.UserServiceImpl * *..UserServiceImpl.*(..) -
包切入点表达式(实战中用的较多)
指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能。

➢ 语法1
# 切入点包中的所有类,必须在proxy中,不能在proxy包的子包中 * com.lyc.proxy.*.*(..)➢ 语法2
# 切入点当前包及其子包都生效 * com.lyc.proxy..*.*(..)
2️⃣ 切入点函数
切入点函数:用于执行切入点表达式。
-
execution➢ 最为重要的切入点函数,功能最全。
➢ 执行:方法切入点表达式、类切入点表达式、包切入点表达式。
➢ 弊端:execution执行切入点表达式 ,书写麻烦:
execution(* com.lyc.proxy..*.*(..))➢ 注意:其他的切入点函数 简化是execution书写复杂度,功能上完全一致。
-
args➢ 作用:主要用于函数(方法)参数的匹配
➢ 切入点:方法参数必须得是2个字符串类型的参数
➢
execution(* *(String,String))➢
args(String,String) -
within➢ 作用:主要用于进行类、包切入点表达式的匹配
➢ 切入点:UserServiceImpl这个类、com.lyc.proxy这个包
➢
execution(* *..UserServiceImpl.*(..))➢
within(*..UserServiceImpl)➢
execution(* com.lyc.proxy..*.*(..))➢
within(com.lyc.proxy..*) -
@annotation➢ 作用:为具有特殊注解的方法加入额外功能
<aop:pointcut id="" expression="@annotation(com.lyc.Log)"/> -
切入点函数的逻辑运算
指的是 整合多个切入点函数一起配合工作,进而完成更为复杂的需求。
➢ and与操作
案例: 方法名:login 参数: 2个字符串 1. execution(* login(String,String)) 2. execution(* login(..)) and args(String,String) 注意:与操作不能用于同种类型的切入点函数 案例:register方法 和 login方法作为切入点 execution(* login(..)) or execution(* register(..))➢ or或操作
案例:register方法 和 login方法作为切入点 execution(* login(..)) or execution(* register(..))
4. AOP编程
Ⅰ. AOP概念
AOP(Aspect Oriented Programing) 面向切面编程
切面 = 切入点 + 额外功能
面向切面编程 = Spring动态代理开发
-
AOP的概念
➢ 本质就是Spring的动态代理开发,通过代理类为原始类增加额外功能。
➢ 好处:利于原始类的维护。
-
注意:AOP编程不可能取代OOP(面向对象编程),是OOP编程的有意补充。
Ⅱ. AOP编程的开发步骤
本质就是Spring的动态代理开发。
- 原始对象
- 额外功能(MethodInterceptor)
- 切入点
- 组装切面(额外功能 + 切入点)
Ⅲ. 切面的名词解释
切面 = 切入点 + 额外功能几何学:面 = 点 + 相同的性质

5. AOP的底层实现原理
Ⅰ. 核心问题
-
AOP如何创建动态代理类
➢ 动态字节码技术
-
Spring工厂如何加工创建代理对象
➢ 通过原始对象的id值,获得的是代理对象
Ⅱ. 动态代理类的创建
1️⃣ JDK的动态代理
Proxy.newProxyInstance()方法参数详解


-
编码
public class TestJDKProxy { /* 1. 借用类加载器 TestJDKProxy 借哪个都可以 UserServiceImpl 2. JDK8.x前 内部类访问外部类局部变量 要将局部变量声明为final final UserService userService = new UserServiceImpl(); */ public static void main(String[] args) { // 1.创建原始对象 UserService userService = new UserServiceImpl(); // 2.JDK创建动态代理 // 2.1内部类实现InvocationHandler接口 调用invoke方法 InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("=====proxy log====="); // 原始方法运行 Object ret = method.invoke(userService, args); return ret; } }; // 2.2调用Proxy.newProxyInstance()返回代理对象 UserService userServiceProxy = (UserService) Proxy.newProxyInstance( TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler); // 3.执行原始方法 userServiceProxy.login("lyc", "123"); userServiceProxy.register(new User()); } }
2️⃣ Cglib的动态代理
Cglib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证二者方法一致,同时在代理类中提供新的实现(额外功能 + 原始方法)。

-
Cglib编码
package com.lyc.cjlib; import com.lyc.proxy.User; import org.springframework.cglib.proxy.Enhancer; // 此时用到的MethodInterceptor是cglib包下的 之前用的在aop包下 import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class TestCglib { public static void main(String[] args) { // 1.创建原始对象 UserService userService = new UserService(); /* 2.通过Cglib方式创建动态代理对象 Proxy.newProxyInstance(classloader,interface,invocationhandler) Enhancer.setClassLoader() →↑ Enhancer.setSuperClass() →↑ Enhancer.setCallback() => MethodInterceptor(cglib) →↑ Enhancer.create() => 代理 */ Enhancer enhancer = new Enhancer(); // 2.1借用类加载器 enhancer.setClassLoader(TestCglib.class.getClassLoader()); // 2.2提供原始对象(父类)的class对象 enhancer.setSuperclass(userService.getClass()); // 2.3提供额外功能 MethodInterceptor interceptor = new MethodInterceptor() { // 等同于 InvocationHandler => invoke() @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("----cglib log----"); // 执行原始方法 Object ret = method.invoke(userService, args); return ret; } }; enhancer.setCallback(interceptor); // 3.创建代理 UserService userServiceProxy = (UserService) enhancer.create(); // 4.调用方法 userServiceProxy.login("lyc", "123"); userServiceProxy.register(new User()); } } -
总结
➢
JDK动态代理: Proxy类 Proxy.newProxyInstance() 通过接口创建代理的实现类➢
Cglib动态代理: Enhancer类 Enhancer.setXx() 通过继承父类创建的代理类
Ⅲ. Spring工厂如何加工原始对象
-
再谈BeanPostProcessorAOP的底层实现

BeanPostProcessor就是对Spring工厂创建的对象进行加工,在前面开发Spring动态代理的过程中,获取到Spring的ApplicationContext工厂后,通过原始对象的id属性实际上获取的是代理对象,实际上Spring就是通过在BeanPostProcessor的After方法中对于传来的bean对象通过代理模式为其生成一个代理对象,最后返回的是这个代理对象,所以我们最后通过原始类的id属性获取的是代理对象。
-
编码
工厂代理类public class ProxyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /* Proxy.newProxyInstance(); */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("=====BeanPostProcessor's new log====="); Object ret = method.invoke(bean, args); return ret; } }; return Proxy.newProxyInstance( ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler); } }配置文件applicationContext.xml<bean id="userService" class="com.lyc.factory.UserServiceImpl"></bean> <!-- 1. 实现BeanPostProcessor 进行加工 2. 配置文件中对BeanPostProcessor进行配置 --> <bean id="proxyBeanPostProcessor" class="com.lyc.factory.ProxyBeanPostProcessor"></bean>测试成功

6. 基于注解的AOP编程
Ⅰ. 基于注解的AOP编程的开发步骤
- 原始对象
- 额外功能
- 切入点
- 组装切面
而AOP的注解编程,将后三步直接整合到了一起
只需要两步
- 原始对象
- 定义切面类
-
编码
切面类package com.lyc.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; /* 1.额外功能 public class MyAround implements MethodInterceptor { public Object invoke(MethodInvocation invocation) { Object ret = invocation.proceed(); return ret; } } 2.切入点 <aop:config> <aop:pointcut id="" expression="execution(* login(..))"/> </aop:config> */ // 使用Aspect注解,表示这个类是一个切面类 在切面类里可以定义很多的切入点+额外功能 @Aspect public class MyAspect { // 使用Around注解表示这是额外功能,类似于动态代理的invoke方法,返回值就是原始对象的返回值 // 在注解里面直接定义切入点表达式 // 注意方法的修饰符和返回值必须是 public + Object 方法名可以自定义 // 参数 ProceedingJoinPoint 等同于原来的 MethodInvocation 此自定义方法等同于之前的invoke()方法 @Around("execution(* login(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("====aspect log===="); Object ret = joinPoint.proceed(); return ret; } } /* 通过切面类 定义了 额外功能 @Around 定义了 切入点 @Around("execution(* login(..))") @Aspect 切面类 */配置文件applicationContext.xml<bean id="userService" class="com.lyc.aspect.UserServiceImpl"></bean> <!-- 切面 1. 额外功能 2. 切入点 3. 组装切面 --> <bean id="around" class="com.lyc.aspect.MyAspect"></bean> <!-- 告知Spring基于注解进行AOP编程 --> <aop:aspectj-autoproxy/>测试public class TestAspectProxy { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext2.xml"); UserService userService = (UserService) ctx.getBean("userService"); userService.login("lyc", "csgo"); userService.register(new User()); } }只有login方法加入了额外功能

Ⅱ. 细节
-
切入点复用
@PointCut注解把切入点的配置 提取到一个独立的方法上面,打上@Pointcut注解;定义切入点表达式,后续更加有利于切入点复用。
@Aspect public class MyAspect { // 提取切入点至@PointCut注解中 利于代码的后续维护 // 权限修饰符和返回值必须是 public + void 方法名任意 方法体可以为空 @Pointcut("execution(* login(..))") public void myPointCut() {} // 直接引用方法名 @Around("myPointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("====aspect log===="); Object ret = joinPoint.proceed(); return ret; } @Around("myPointCut()") public Object around1(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("====aspect tx===="); Object ret = joinPoint.proceed(); return ret; } } -
动态代理的创建方式
AOP底层实现 2种代理创建方式 1. JDK 通过实现接口 做新的实现类方式 创建代理对象 2. Cglib 通过继承父类 做新的子类 创建代理对象 默认情况 AOP编程 底层应用JDK动态代理创建方式 如果切换Cglib 1. 基于注解AOP开发 <aop:aspectj-autoproxy proxy-target-class="true" /> 2. 传统的AOP开发 <aop:config proxy-target-class="true"> </aop:config>
7. AOP开发中的一个坑
Ⅰ. 问题描述
在同一个业务类中,业务方法间进行调用,如方法a调用了方法b,两个方法都增加了额外功能,但是最后在实际调用过程中只有a方法的额外功能实现了,b没有实现。
public void a() {
b();
}
public void b() {
...
}
原因:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
// 内部调用login方法
this.login("lyc", "csgo");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
myPointCut方法的注解需要改成@Pointcut("execution(* *..UserServiceImpl.*(..))"),UserServiceImpl类中所有的方法都会加上额外功能
测试
public class TestAspectProxy {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext2.xml");
UserService userService = (UserService) ctx.getBean("userService");
//userService.login("lyc", "csgo");
userService.register(new User());
}
}
执行结果,只有register方法加入了额外功能,login方法未加上额外功能:

这是因为在Test中调用register方法的实际是Spring创建的代理对象Proxy,而在register方法中调用login方法的是this对象UserServiceImpl,这只是个原始类,只执行核心功能。
Ⅱ. 解决方案
实现ApplicationContextAware接口重写setApplicationContext()方法,将ApplicationContext作为成员变量在setApplicationContext()方法中进行赋值,在调用中使用ApplicationContext对象获取代理对象执行方法。
注意: 不要在方法中创建ApplicationContext对象,因为这个对象代表了Spring工厂,占用资源多,一个程序中应该只存在一个。
// 如果想让内层的方法也调用代理对象的方法,就要实现AppicationContextAware接口获得工厂,进而获得代理对象
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
// 获取工厂
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
// 调用的是原始对象的login方法 => 核心功能
/*
设计目的:代理对象的login方法 => 额外功能+核心功能
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login();
Spring工厂重量级资源 一个应用中 应该只创建一个工厂
*/
UserService userService = (UserService) ctx.getBean("userService");
userService.login("lyc", "csgo");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
测试
两个方法都加上了额外功能

8. AOP阶段知识总结


1991

被折叠的 条评论
为什么被折叠?



