AOP编程
?、静态代理设计模式
1. 为什么需要代理设计模式
1.1 问题
2. 代理设计模式
1.1 概念
通过代理类,为原始类(?标)增加额外的功能
好处:利于原始类(?标)的维护
1.2名词解释
- ?标类 原始类 —
被代理类 指的是 业务类 (核?功能 --> 业务运算 DAO调?) - ?标?法,原始?法
?标类(原始类)中的?法 就是?标?法(原始?法) - 额外功能 (附加功能)
?志,事务,性能
1.3 代理开发的核?要素
代理类 = ?标类(原始类) + 额外功能 + 原始类(?标类)实现相同的接?
房东 ---> public interface UserService{
m1
m2
}
UserServiceImpl implements UserService{
m1 ---> 业务运算 DAO调?
m2
}
UserServiceProxy implements UserService{
m1
m2
}
1.4 编码
静态代理:为每?个原始类,??编写?个代理类 (.java .class)
1.5 静态代理存在的问题
- 静态类?件数量过多,不利于项?管理
UserServiceImpl —> UserServiceProxy OrderServiceImpl —> OrderServiceProxy - 额外功能维护性差
代理类中 额外功能 修改复杂(麻烦)
?、Spring的动态代理开发
1. Spring动态代理的概念
概念:通过代理类为原始类(?标类)增加额外功能 好处:利于原始类(?标类)的维护
2. 搭建开发环境
<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>
3. Spring动态代理的开发步骤
-
创建原始对象(?标对象) 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="com.baizhiedu.proxy.UserServiceImpl"/>
-
额外功能 MethodBeforeAdvice接? 额外的功能书写在接?的实现中,运?在原始?法执?之前运?额外功能。 public class Before implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----method before advice log------");
}
}
<bean id="before" class="com.baizhiedu.dynamic.Before"/>
-
定义切?点 切?点:额外功能加?的位置 ?的:由程序员根据??的需要,决定额外功能加?给那个原始?法 register login 简单的测试:所有?法都做为切?点 ,都加?额外的功能。 <aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
</aop:config>
-
组装 (2/3步整合) 表达的含义:所有的?法 都加? before的额外功能 <aop:advisor advice-ref="before" pointcut-ref="pc"/>
-
调? ?的:获得Spring??创建的动态代理对象,并进?调? ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
注意:
- Spring的??通过
原始对象的id值获得的是代理对象 - 获得代理对象后,可以通过
声明接?类型 ,进?对象的存储 UserService userService=(UserService)ctx.getBean("userService");
userService.login("");
userService.register();
4.动态代理细节分析
-
Spring创建的动态代理类在哪?? Spring框架在运?时,通过动态字节码技术 ,在JVM创建的,运?在JVM内部 ,等程序结束后,会和JVM?起消失 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进?创建对象,当虚拟机结束,动态字节码跟着消失。 结论:动态代理不需要定义类?件,都是JVM运?过程中动态创建 的,所以不会造成静态代理,类?件数量过多,影响项?管理的问题。
-
动态代理编程简化代理的开发 在额外功能不改变的前提下,创建其他?标类(原始类)的代理对象时,只需要指定原始(?标)对象即可。 -
动态代理额外功能的维护性??增强
三、Spring动态代理详解
1. 额外功能的详解
-
MethodBeforeAdvice分析
-
MethodBeforeAdvice接? 作?:额外功能运?在原始?法执?之前 ,进?额外功能操作。 public class Before1 implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----new method before advice log------");
}
}
-
before?法的3个参数在实战中,该如何使?。 before?法的参数,在实战中,会根据需要进?使?,不?定都会?到,也有可能都不? 。 Servlet{
service(HttpRequest request,HttpResponse response){
request.getParameter("name") -->
response.getWriter() --->
}
}
-
MethodInterceptor(?法拦截器)推荐 methodinterceptor接? :额外功能可以根据需要运?在原始?法执? 前、后、前后 。 public class Arround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("-----额外功能 log----");
Object ret = invocation.proceed();
return ret;
}
}
额外功能运?在原始?法执?之后 @Override
public Object invoke(MethodInvocation invocation) throws Throwable{
Object ret = invocation.proceed();
System.out.println("-----额外功能运?在原始?法执?之后----");
return ret;
}
额外功能运?在原始?法执?之前,之后 什么样的额外功能 运?在原始?法执?之前,之后都要添加?事务 @Override
public Object invoke(MethodInvocation invocation) throws Throwable{
System.out.println("-----额外功能运?在原始?法执?之前----");
Object ret = invocation.proceed();
System.out.println("-----额外功能运?在原始?法执?之后----");
return ret;
}
额外功能运?在原始?法抛出异常 的时候 @Override
public Object invoke(MethodInvocation invocation) throws Throwable{
Object ret = null;
try {
ret = invocation.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;
}
2. 切?点详解
切?点决定额外功能加?位置(?法)
<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection( (…)) —> 匹配了所有?法 a b c
- execution() 切?点函数
- * *(…) 切?点表达式
2.1 切?点表达式
-
?法 切?点表达式 * *(..) --> 所有?法
* ---> 修饰符 返回值
* ---> ?法名
()---> 参数表
..---> 对于参数没有要求 (参数有没有,参数有?个都?,参数是什么类型的都?)
-
定义login?法作为切?点 * login(..)
# 定义 register 作为切?点
* register(..)
-
定义login?法且login?法有两个字符串类型的参数 作为切?点 * login(String,String)
# # 注意:? java.lang 包中的类型,必须要写 全限定名
* register(com.achang.proxy.User)
# .. 可以和具体的参数类型连?
* login(String,..) -->
login(String),login(String,String),login(String,com.baizhiedu.proxy.User)
-
精准?法切?点限定 修饰符 返回值 包.类.?法(参数)
* com.achang.proxy.UserServiceImpl.login(..)
* com.achang.proxy.UserServiceImpl.login(String,String)
-
类 切?点· 指定特定类作为切?点(额外功能加?的位置),?然这个类中的所有?法,都会加上对应的额外功能
-
包 切?点表达式 实战 指定包作为额外功能加?的位置,?然包中的所有类及其?法都会加?额外的功能
2.2 切?点函数
切?点函数:?于执?切?点表达式
-
execution 最为重要的切?点函数,功能最全。
- 执??法切?点表达式、类切?点表达式、包切?点表达式
弊端:execution执?切?点表达式 ,书写麻烦
execution(* com.baizhiedu.proxy..*.*(..))
注意:其他的切?点函数 简化是execution书写复杂度,功能上完全?致
-
args 作?:主要?于函数(?法) 参数的匹配
切?点:?法参数必须得是2个字符串类型的参数
execution( * * (String,String))
args(String,String)
-
within 作?:主要?于进?类、包切?点表达式的匹配
切?点:UserServiceImpl这个类
execution( * * ..UserServiceImpl. *(..))
within( *..UserServiceImpl)
execution( * com.achang.proxy..* . *(..))
within(com.achang.proxy..*)
-
@annotation 作?:为具有特殊注解的?法加?额外功能 <aop:pointcut id="pc" expression="@annotation(com.baizhiedu.Log)"/>
组装中的arround,自定义注解的实现,通过实现MethodInterceptor ;pc是自定义注解 -
切?点函数的逻辑运算 指的是 整合多个切?点函数?起配合?作,进?完成更为复杂的需求
-
and与 操作 案例:login 同时 参数 2个字符串
1. execution( * login(String,String))
2. execution( * login(..)) and args(String,String)
注意:与操作不同?于同种类型的切?点函数
案例:register?法 和 login?法作为切?点 xxxxxxxxxxxxxxxxxxxx
execution( * login(..)) or execution(* register(..))
-
or或 操作 案例:register?法 和 login?法作为切?点
execution( * login(..)) or execution(* register(..))
四、AOP编程
1. AOP概念
本质就是Sprind的动态代理开发 ,通过代理类为原始类增加额外功能 。 好处:利于原始类的维护 注意:AOP编程不可能取代OOP,OOP编程有意补充 。
-
AOP (Aspect Oriented Programing) ?向切?编程 = Spring动态代理开发 以切?为基本单位的程序开发 ,通过切?间的彼此协同,相互调?,完成程序的构建 切? = 切?点 + 额外功能 -
OOP (Object Oriented Programing) ?向对象编程 Java 以对象为基本单位的程序开发 ,通过对象间的彼此协同,相互调?,完成程序的构建 -
POP (Producer Oriented Programing) ?向过程(?法、函数)编程 C 以过程为基本单位的程序开发 ,通过过程间的彼此协同,相互调?,完成程序的构建
2. AOP编程的开发步骤
- 原始对象
- 额外功能 (MethodInterceptor)
- 切?点
- 组装切? (额外功能+切?点)
3. 切?的名词解释
- 切? = 切?点 + 额外功能
?何学 - ? = 点 + 相同的性质
五、AOP的底层实现原理
1. 核?问题
- AOP如何创建动态代理类(动态字节码技术)
- Spring??如何加?创建代理对象
通过原始对象的id值,获得的是代理对象
2. 动态代理类的创建
2.1 JDK的动态代理
- Proxy.newProxyInstance?法参数详解
public class TestJDKProxy {
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 --------");
Object ret = method.invoke(userService, args);
return ret;
}
};
UserService userServiceProxy =(UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler);
userServiceProxy.login("achang", "123456");
userServiceProxy.register(new User());
}
}
2.2 CGlib的动态代理
CGlib创建动态代理的原理:??继承关系 创建代理对象,原始类作为?类,代理类作为?类,这样既可以保证2者?法?致,同时在代理类中提供新的实现(额外功能+原始?法)
package com.achang.cglib;
import com.achang.proxy.User;
import org.springframework.cglib.proxy.Enhancer;
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) {
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
@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);
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login("achang", "1233456");
userServiceProxy.register(new User());
}
}
- JDK动态代理 Proxy.newProxyInstance() 通过
接? 创建代理的实现类 - Cglib动态代理 Enhancer 通过
继承 ?类创建的代理类
3. Spring??如何加?原始对象
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException {
return bean;
}
@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("----- new Log-----");
Object ret = method.invoke(bean, args);
return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
}
}
<bean id="userService" class="com.baizhiedu.factory.UserServiceImpl"/>
<bean id="proxyBeanPostProcessor" class="com.achang.factory.ProxyBeanPostProcessor"/>
六、基于注解的AOP编程
1. 基于注解的AOP编程的开发步骤
- 原始对象
- 额外功能
- 切?点
- 组装切?
# 通过切?类 定义了 额外功能 @Around
定义了 切?点 @Around("execution(* login(..))")
@Aspect 切?类
package com.achang.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyAspect {
@Around("execution(* login(..))")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----aspect log ------");
Object ret = joinPoint.proceed();
return ret;
}
}
<bean id="userService" class="com.achang.aspect.UserServiceImpl"/>
<bean id="arround" class="com.achang.aspect.MyAspect"/>
<aop:aspectj-autoproxy />
2. 细节
-
切?点复? 在切?类中定义?个函数 上?@Pointcut 注解 通过这种?式,定义切?点表达式,后续更加有利于切?点复?。 @Aspect
public class MyAspect {
@Pointcut("execution(* login(..))")
public void myPointcut(){}
@Around(value="myPointcut()")
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----aspect log ------");
Object ret = joinPoint.proceed();
return ret;
}
@Around(value="myPointcut()")
public Object arround1(ProceedingJoinPoint joinPoint)
throws Throwable {
System.out.println("----aspect tx ------");
Object ret = joinPoint.proceed();
return ret;
}
}
-
动态代理的创建?式 AOP底层实现 2种代理创建?式
JDK 通过实现 接? 做新的实现类?式 创建代理对象Cglib 通过继承 ?类 做新的?类 创建代理对象
默认情况 AOP编程 底层应?JDK动态代理 创建?式 如何切换Cglib??? proxy-target-class="true" ,即可切换为Cglib #1. 基于注解AOP开发
<aop:aspectj-autoproxy proxy-target-class="true"/></aop>
#2. 传统的AOP开发
<aop:config proxy-target-class="true"></aop>
七、AOP开发中的?个坑
-
坑: 在同?个业务类中,进?业务?法间的相互调?,只有最外层的?法,才是加?了额外功能(内部的?法,通过普通的?式调?,都调?的是原始?法)。 如果想让内层的?法也调?代理对象的?法,就要AppicationContextAware获得?? ,进?获得代理对象 。
public class UserServiceImpl implements UserService,ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Log
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
UserService userService = (UserService)ctx.getBean("userService");
userService.login("suns", "123456");
}![image-20210905211916373](C:/Users/PePe/AppData/Roaming/Typora/typora-user-images/image-20210905211916373.png)
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
?、AOP阶段知识总结
|