一、SpringAOP理解
1. AOP概念
AOP(Aspect-Oriented Programming:面向切面编程) AOP代表的是一个横向的关系,剖开对象的内部,并且把影响多个类的共同行为抽取出来,作为公共模块(叫做切面Aspect),然后再通过织入的方式把这个切面放进去。
理解来说:就是能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
2. AOP底层原理
AOP底层是通过动态代理来实现的,同时有JDK动态代理和CGLIB动态代理两种方式:
① 有接口的情况,使用 JDK 动态代理,即创建接口实现类代理对象,增强类的方法。
② 没有接口的情况,使用 CGLIB 动态代理,即创建子类的代理对象,增强类的方法。
3. JDK proxy VS CGLIB proxy
语法上:
① JDK动态代理需要目标类有父接口
② JDK动态代理产生的代理对象是目标类的兄弟
③ CGLIB动态代理不需要父接口,但是目标类不能被final修饰④ CGLIB动态代理产生的代理对象是目标类的子类
性能上:
① JDK产生代理对象的性能较高;CGLIB较低② JDK动态代理调用方法的性能较低;CGLIB较高
③ 两者的适用场合不同:JDK动态代理适合于频繁创建的场景;
如果是单例或对象池的场景(无需频繁创建代理对象),但需要频繁调用代理方法,优先考虑CGLIB动态代理。
二、静态代理实现
静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。
就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
1. 创建售卖接口ISale
public interface ISale {
//卖烧饼
void saleShaoBing();
//卖煎饼
void saleJianBing();
//卖月饼
void saleYueBing();
//卖馒头
void saleManTou();
}
2. 创建WuDa类实现接口ISale
public class WuDa implements ISale{
@Override
public void saleShaoBing() {
System.out.println("卖烧饼...");
}
@Override
public void saleJianBing() {
System.out.println("卖煎饼...");
}
@Override
public void saleYueBing() {
System.out.println("卖月饼...");
}
@Override
public void saleManTou() {
System.out.println("卖馒头...");
}
}
3. 创建WuDaProxy代理类增强实现接口ISale
WuDa类本来只能卖四样东西,WuDaProxy代理类对其中几个售卖的免费送大麦茶。
这样代理商不但更快更多的售卖,而且代理商和武大俩人都得到了更多的利润,这一举措就是增强的实现。
public class WuDaProxy implements ISale{
private WuDa target ;
public WuDaProxy(WuDa target){
this.target = target ;
}
@Override
public void saleShaoBing() {
target.saleShaoBing();
song();
}
@Override
public void saleJianBing() {
target.saleJianBing();
song();
}
@Override
public void saleYueBing() {
target.saleYueBing();
song();
}
@Override
public void saleManTou() {
target.saleManTou();
}
private void song(){
System.out.println("送大麦茶...");
}
}
三、动态代理JDK实现
JDK动态代理的前提是:目标类必须要有父接口
1. 创建售卖接口ISale
public interface ISale {
//卖烧饼
void saleShaoBing();
//卖煎饼
void saleJianBing();
//卖月饼
void saleYueBing();
//卖馒头
void saleManTou();
}
2. 创建WuDa类实现接口ISale
public class WuDa implements ISale{
@Override
public void saleShaoBing() {
System.out.println("卖烧饼...");
}
@Override
public void saleJianBing() {
System.out.println("卖煎饼...");
}
@Override
public void saleYueBing() {
System.out.println("卖月饼...");
}
@Override
public void saleManTou() {
System.out.println("卖馒头...");
}
}
3. 继承接口InvocationHandler创建代理类的调用处理程序
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;
在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
public class WuDaInvocationHandler implements InvocationHandler {
private ISale target ;
public WuDaInvocationHandler(ISale target){
this.target = target ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName() ;
if(methodName.endsWith("Bing")){
song();
}
return method.invoke(target,args);
}
private void song(){
System.out.println("送大麦茶....");
}
}
4. 测试过程
通过Proxy类的WuDaInvocationHandler方法创建代理对象,我们来看下方法中的参数
① 第一个参数:target.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
② 第二个参数:target.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
③ 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
public class Test {
public static void main(String[] args) {
//1.创建目标对象
ISale target = new WuDa();
//2.创建handler对象(handler:处理器。handler中包含了两个信息:① 赠送的内容 ② 何时赠送 )
WuDaInvocationHandler handler = new WuDaInvocationHandler(target);
//newProxyInstance:创建代理实例对象
//三个参数:
//① 目标类的类加载器。 ② 目标类的父接口 ③ handler
//只有拥有了上面三个"原料",我们Proxy才能创建出代理对象
ISale proxy = (ISale) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
proxy.saleShaoBing();
}
}
四、动态代理CGLIB实现
CGLIB动态代理的前提是:目标类不能被final修饰
1. 创建WuDa类
public class WuDa{
public void saleShaoBing() {
System.out.println("卖烧饼...");
}
public void saleJianBing() {
System.out.println("卖煎饼...");
}
public void saleYueBing() {
System.out.println("卖月饼...");
}
public void saleManTou() {
System.out.println("卖馒头...");
}
}
2. 实现MethodInterceptor 接口创建代理类
实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。
public class WuDaInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
if(methodName.endsWith("Bing")){
song();
}
methodProxy.invokeSuper(o,args);
return null;
}
private void song(){
System.out.println("送大麦茶....");
}
}
3. 测试过程
public class Test {
public static void main(String[] args) {
//1.创建目标对象
WuDa target = new WuDa();
//2.创建interceptor对象
WuDaInterceptor interceptor = new WuDaInterceptor() ;
//3.创建Enhancer对象,它以目标类和interceptor作为原料,生产出代理对象
Enhancer enhancer = new Enhancer();
//此处为什么是superClass???
//因为CGLIB产生的代理对象是目标类的子类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(interceptor);
WuDa proxy = (WuDa) enhancer.create();
//proxy.saleShaoBing();
proxy.saleManTou();
}
}