Spring AOP 编程
1. 静态代理设计模式(手动)
为什么需要代理设计模式
JavaEE分层开发中,最为重要的是Service层
Service层中包含了哪些代码?
Service层中 = 核⼼功能(⼏⼗⾏ 上百代 码) + 额外功能(附加功能)
-
核⼼功能
- 业务运算
- DAO调⽤
-
额外功能 不属于业务 可有可⽆ 代码量很⼩
- 事务、⽇志、性能
额外功能书写在Service层中好不好?
Service层的调⽤者的⻆度(Controller):需要在Service层书写额外功能。
软件设计者:Service 层不需要额外功能
解决方式:引入一个代理(中介)类,完成额外功能,并调用目标类(原始类)的核心功能
代理设计模式
通过代理类,为原始类(⽬标)增加额外的功能
好处:利于原始类(⽬标)的维护
⽬标类 原始类
指的是 业务类 (核⼼功能 --业务运算 DAO调⽤)
⽬标⽅法,原始⽅法
⽬标类(原始类)中的⽅法 就是⽬标⽅法 (原始⽅法)
额外功能 (附加功能)
⽇志,事务,性能
代理开发的核⼼要素
代理类 = ⽬标类(原始类) + 额外功能 + 原始类(⽬标类)实现相同的接⼝
/**
* 代理类
* 1. 与被代理类实现相同接口
* 2. 必须原始对象
* 3. 实现额外功能
*
* @author duniqb <duniqb@qq.com>
* @version v1.0.0
* @date 2020/7/1 10:52
* @since 1.8
*/
public class UserServiceProxy implements UserService {
/**
* 原始对象
*/
private UserServiceImpl userService = new UserServiceImpl();
@Override
public void register(User user) {
// 额外功能
System.out.println("----log-----");
userService.register(user);
}
@Override
public boolean login(String name, String password) {
System.out.println("----log-----");
return userService.login(name, password);
}
}
静态代理存在的问题
静态类⽂件数量过多,不利于项⽬管理
UserServiceImpl
UserServiceProxy
额外功能维护性差
代理类中 额外功能修改复杂(麻烦)
2. Spring的动态代理开发
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;
}
}
<bean id="userService" class="cn.duniqb.aop.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------");
}
}
<bean id="before" class="cn.duniqb.aop.proxy.dynamic.Before"/>
3. 定义切⼊点
切⼊点:额外功能加⼊的位置
⽬的:由程序员根据⾃⼰的需要,决定额外功能加⼊给那个原始⽅法
<aop:config proxy-target-class="true">
<!-- 所有的方法,都作为切入点 -->
<aop:pointcut id="all" expression="execution(* *(..))"/>
</aop:config>
4. 组装 (2 3整合)
表达的含义:所有的⽅法 都加⼊ before的额外功能
<aop:config proxy-target-class="true">
<!-- 所有的方法,都作为切入点,加入额外功能 login register -->
<aop:pointcut id="all" expression="execution(* *(..))"/>
<!-- 组装:目的把切入点与额外功能进行整合 -->
<aop:advisor advice-ref="before" pointcut-ref="all"/>
</aop:config>
5. 调⽤
⽬的:获得Spring⼯⼚创建的动态代理对象,并进⾏调⽤
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
注意:
-
Spring的⼯⼚通过原始对象的 id值获得的是代理对象
-
获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储
UserService userService = (UserService) ctx.getBean("user Service");
userService.login("")
userService.register()
动态代理细节分析
Spring创建的动态代理类在哪⾥?
Spring框架在运⾏时,通过动态字节码技术,在JVM创建的,运⾏在JVM内部,等程序结束后,会和JVM⼀起消失
什么叫动态字节码技术
通过第三方动态字节码框架,在JVM中创建对应类的字节码,进⽽创建对象,当虚拟机结束,动态字节码跟着消失。
结论
动态代理不需要定义类⽂件,都是JVM运⾏过程中动态创建的,所以不会造成静态代理,类⽂件数量过多,影响项⽬管理的问题。
动态代理编程简化代理的开发
在额外功能不改变的前提下,创建其他⽬标类(原始类)的代理对象时,只需要指定原始(⽬标)对象即可。
动态代理额外功能的维护性⼤⼤增强
3. Spring动态代理详解
额外功能的详解
MethodBeforeAdvice分析
MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作。
public class Before1 implements MethodBeforeAdvice {
/**
* 需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
*
* @param method 额外功能所增加给的那个原始方法 login方法
* @param args 额外功能所增加给的那个原始方法的参数。String name,String password,User
* @param target 额外功能所增加给的那个原始对象 UserServiceImpl
*/
@Override
public void before(Method method, Object[] args, Object target) {
System.out.println("-----new method before advice log------");
}
}
MethodInterceptor(⽅法拦截器)
MethodInterceptor 接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、前后。
public class Around implements MethodInterceptor {
/**
* 作用:额外功能书写在invoke
* 额外功能 原始方法之前
* 原始方法之后
* 原始方法执行之前 之后
*
* @param invocation 额外功能所增加给的那个原始方法
* @return 原始方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("------log-----");
return invocation.proceed();
}
}
4. AOP 编程
概念
AOP (Aspect Oriented Programing)
⾯向切⾯编程 = Spring动态代理开发
以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
切⾯ = 切⼊点 + 额外功能
OOP (Object Oritened Programing)
⾯向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建
POP (Producer Oriented Programing)
⾯向过程(⽅法、函数)编程 C
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建
AOP的概念:
本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。
AOP编程的开发步骤
-
原始对象
-
额外功能 (MethodInterceptor)
-
切⼊点
-
组装切⾯ (额外功能+切⼊点)
切⾯的名词解释
切⾯ = 切⼊点 + 额外功能
⼏何学 ⾯ = 点 + 相同的性质
5. AOP的底层实现原理
核⼼问题
AOP如何创建动态代理类
动态字节码技术
Spring⼯⼚如何加⼯创建代理对象
通过原始对象的id值,获得的是代理对象
动态代理类的创建
JDK的动态代理
Proxy.newProxyInstance(classloader, interfaces, invocationHandler);
代理创建三要素:
-
原始对象
classloader,借用一个类加载器,创建代理类的Class 对象,进而可以创建代理对象
-
额外功能
invocationHandler
-
interfaces
代理对象和原始对象实现相同的接口
userService.getClass().getInterfaces();
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------proxy log --------");
// 原始方法运行
return method.invoke(userService, args);
}
};
// 借用类加载器加载原始对象, 代理对象和原始对象实现相同的接口, 额外功能
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
userServiceProxy.login("duniqb", "123456");
userServiceProxy.register(new User());
}
CGlib的动态代理
CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,这样既可以保证l两者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)
public class TestCglib {
public static void main(String[] args) {
// 创建原始对象
UserService userService = new UserService();
// 通过cglib方式创建动态代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
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----");
return method.invoke(userService, args);
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("suns", "123345");
userServiceProxy.register(new User());
}
}
动态代理总结
-
JDK 动态代理
Proxy.newProxyInstance() 通过接⼝ 创建代理的实现类
-
Cglib 动态代理
Enhancer 通过继承⽗类创建的代理类
Spring⼯⼚如何加⼯原始对象
6. 基于注解的AOP编程
基于注解的AOP编程的开发步骤
原始对象
额外功能
切⼊点
组装切⾯