1.Spring模块
2.Spring AOP的实现原理?
(1)AOP(Aspect Oriented Programming):面向切面编程
什么是面向切面编程:程序在运行期间,动态地将某段代码插入到原来方法代码的某些位置中。
AOP :用于处理系统中分布的各个模块的横切关注点,比如说事务管理、日志、缓存等。它是使用动态代理来实现的,在内存中临时为方法生成了一个AOP对象,这个对象包含目标对象的所有方法,在特定的切点做了增强处理,并回调原来的方法。
(2)Spring AOP的动态代理主要有两种方式:
1.JDK动态代理(Spring默认使用)
2.cglib动态代理
JDK动态代理:通过反射来接收被代理的类,不过被代理的类必须实现一个接口,核心是InvocationHandler和Proxy类(Proxy是反射包下的)
package com.atguigu.proxy.factory;
import com.atguigu.pojo.AImpl;
import com.atguigu.pojo.Calculate;
import com.atguigu.pojo.Calculator;
import com.atguigu.pojo.IA;
import com.atguigu.utils.LogUtils;
import javax.imageio.IIOImage;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JdkProxyFactory {
/**
* 通过Jdk底层自带的Jdk动态代理技术解决前面的日记需要 <br/>
* @param target
* @return
*/
public static Object createJdkProxy(Object target){
/**
* Proxy 是Jdk中自带的一个工具类(反射包下,属于反射的功能). <br/>
* Proxy类的作用: 它可以帮我们创建代理类或实例 <br/>
* 方法newProxyInstance()说明: 创建代理对象实例 <br/>
* 第一个参数是: 目标对象的类加载器 <br/>
* 第二个参数是: 目标对象实现的所有接口<br/>
* 第三个参数是: InvocationHandler 接口的实例<br/>
* InvocationHandler 接口的实现类可以对代理的目标对象方法进行增强操作. <br/>
* 代理的目标对象 ===>>> 需要额外增加功能的类(对象实例) <br/>
* 增强操作 ===>>> 给原来功能添加的额外功能叫增强操作 ( 日记就是增强操作 )<br/>
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* invoke方法 是 InvocationHandler 接口中唯一的方法. <br/>
* 在代理对象每次调用方法时,都会执行 invoke() 方法 , 我们所有的增强操作都需要在invoke()方法中完成<br/>
* @param proxy 是代理对象实例 <br/>
* @param method 是代理调用的方法的反射 Method 对象实例 <br/>
* @param args 是调用代理方法时传递进来的参数 <br/>
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println(method); // 打印方法信息
// System.out.println( Arrays.asList(args) );// 打印参数信息
Object result = null;
LogUtils.logBefore(method.getName(), args);
try {
// 1 在 invoke() 方法我们要执行代理对象的操作 (加法 或 除法)
// 返回值是 method 方法调用时的返回值
result = method.invoke(target , args);
// 2 以及增强操作( 就是日记操作 )
LogUtils.logAfterReturning(method.getName(),result);
} catch (Exception e) {
LogUtils.logAfterThrowing(method.getName(),e);
}
// invoke() 方法的返回值,就是代理方法的返回值.
return result;
}
}
);
}
public static void main(String[] args) throws Exception {
// // 目标对象
// Calculate target = new Calculator();
//
// // 创建出来了Calculte的代理对象实例
// Calculate calculateProxy = (Calculate) createJdkProxy(target);
// // jdk动态代理对象实例和目标对象实例 同宗同族 ( 他们都实现了相同的接口 )
// System.out.println( "代理方法的结果是 : " + calculateProxy.div(100,0) );
// jdk动态代理创建出来的代理对象实例 是 目标对象 接口的一个实现类
// 这个代理对象 和 目标对象类没有父子关系 ( 只能用接口接收代理对象 )
}
}
cglib: 动态代理的类不需要实现接口,cglib是一个代码生成的类库,可以在运行时动态生成某个类的子类,所以,cglib是通过继承的方式做的动态代理,因此如果一个类被标记为final,那么它是无法使用cglib做动态代理的。
Jdk动态代理是通过实现目标对象所有接口产生一个代理对象实例从而解决问题.
如果目标对象没有接口.则可以使用Cglib动态代理技术. Cglib动态代理技术对目标对象有没有实现接口,没有要求.
Cglib动态代理技术,是通过拷贝然后修改目标对象的类的字节码来产生一个代理对象 而且这个Cglib产生的代理对象实例 是
目标对象的一个子类.
package com.atguigu.proxy.factory;
import com.atguigu.pojo.Calculate;
import com.atguigu.pojo.Calculator;
import com.atguigu.utils.LogUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyFactory {
public static Object createCglibProxy(Object target){
// 是Cglib用于创建代理对象的增强的一个工具类
Enhancer enhancer = new Enhancer();
// Cglib需要对目标对象的Class字节码进行修改.
// Cglib产生的代理对象实例.是目标对象的子类
enhancer.setSuperclass( target.getClass() );
// 只要是代理都会对原来的内容进行增强操作 ( 增强就是在原有功能上 额外添加的功能 )
// setCallback() 设置用于增强 操作的实现类( MethodInterceptor对代理方法进行拦截 )
// 每次只要调用Cglib代理的方法,都会执行 MethodInterceptor 接口中 intercept() 方法
enhancer.setCallback(new MethodInterceptor() {
/**
* intercept() 方法 跟 InvocationHandler接口中 invoke() 功能完全一样
* @param proxy Cglib代理对象实例 <br/>
* @param method 调用方法的反射对象实例 <br/>
* @param args 调用方法时传递的参数 <br/>
* @param methodProxy 代理方法的method代理对象 <br/>
* @return 是代理对象方法的返回值. <br/>
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
LogUtils.logBefore(method.getName(),args);
// 调用目标方法 ( 加法 或 除法 或 具体类的具体方法 )
result = method.invoke( target , args );
// 执行增强的代码
LogUtils.logAfterReturning(method.getName(),result);
} catch (Exception e) {
e.printStackTrace();
LogUtils.logAfterThrowing(method.getName(),e);
}
return result;
}
});
//创建Cglib代理实例
return enhancer.create();
}
public static void main(String[] args) {
// 目标对象
Calculator calculator = new Calculator();
// 创建Cglib代理对象实例
Calculate cglibProxy = (Calculate) createCglibProxy(calculator);
// 调用代理方法时会 执行 MethodInterceptor 接口中 intercept() 方法
int result = cglibProxy.div(100,0);
System.out.println(" 代理方法的结果是: " + result);
// Cglib代理 是 目标的子类执行 MethodInterceptor 接口中 intercept() 方法
// System.out.println( cglibProxy instanceof Calculator );
}
}
优点:在没有接口的情况下,同样可以实现代理的效果。
缺点:同样需要自己编码实现代理全部过程。
AOP编程的专业术语
通知(Advice)
通知就是增强的代码。比如前置增强的代码。后置增强的代码。异常增强代码。这些就叫通知
切面(Aspect)
切面就是包含有通知代码的类叫切面。
横切关注点
横切关注点,就是我们可以添加增强代码的位置。比如前置位置,后置位置,异常位置。和返回值位置。这些都叫横切关注点。
目标(Target)
目标对象就是被关注的对象。或者被代理的对象。
代理(Proxy)
为了拦截目标对象方法,而被创建出来的那个对象,就叫做代理对象。
连接点(Joinpoint)
连接点指的是横切关注点和程序代码的连接,叫连接点。
切入点(pointcut)
切入点指的是用户真正处理的连接点,叫切入点。
在Spring中切入点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
切入点表达式,在代码中可以用来表示切入点.
AOP能做什么:
1、降低模块之间的耦合度
2、使系统容易扩展
3、避免修改业务代码,避免引入重复代码,更好的代码复用
AOP怎么用:
前置通知:某方法调用之前发出通知。
后置通知:某方法完成之后发出通知
返回后通知:方法正常返回后,调用通知。在方法调用后,正常退出发出通知
异常通知:抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行 的通知。在方法调用时,异常退出发出通知
环绕通知:通知包裹在被通知的方法的周围通知。