第一部分:改造之后EasyMall的问题
1.1 问题
改造过后的EasyMall成功解决了耦合的问题,但是在很多地方仍然存在非该层应该实现的功能,造成了 无法“高内聚”的现象,同时存在大量存在重复代码,开发效率低下。
测试类代码:
Controller层方法:
Service层方法:
Service的实现类:
Dao层:
Dao层实现类:
事务的工具类:
像事务处理的代码,仍然需要重复,可以进行提取和进一步的简化,简化的步骤代理设计模式,将这部分代码提取到代理者中,简化层中的代码。
设计模式:
静态代理:
package cn.tedu.staticproxy;
public void 吃();
}
package cn.tedu.staticproxy;
public void 吃(){
}
System.out.println("fbb唱歌。。。");
}
package cn.tedu.staticproxy;
public void 吃() {
fbb.吃();
}
@Override
System.out.println("权限认证:你谁啊????");
System.out.println("记录日志:等我,我记一下来访记录");
import org.junit.Test;
@Test
JJRStaticProxy jjr = new JJRStaticProxy();
jjr.唱歌();
}
静态代理设计模式特点:
优点:
结构清晰 易于理解
缺点:
如果被代理者有多个方法,则代理者也需要开发多个方法,其中往往存在大量重复代码,仍然存在代码重复。
静态代理设计模式解决了软件分层过程中 额外的功能代码侵入模块的问题,将额外的功能代码提取到了代理者中进行,但是静态代理实现的代理者中存在大量重复的代码,并没有解决代码重复问题。所以在真正开发中--包括spring的底层,基本不会使用静态代理。
---------------------------------------------------------------------------------------------------
设计模式详细介绍:
修改EasyMall案例:
在service增加代理类:
1.3 动态代理
第一部分:j'd'k内置的动态代理
jdk的内置动态代理
在jdk中提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者,并且可以通过内置的回调函数指定代理在工作时的执行逻辑,从而实现基于jdk原生api的动态代理机制。
案例:
JDK的动态代理原理:
package cn.tedu.javaproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class JavaProxyTest {
public void test01(){
//被代理者
/**
* interfaces: 要求生成的代理者实现的接口们,通常就是实现和被代理者相同的接口,保证具有和被代理者相同的方法
* 在其中编写代码处理代理者调用方法时的回调过程,通常在这里调用真正对象身上的方法,并且在方法之前或之后做额外操作。
SJSkill proxy = (SJSkill) Proxy.newProxyInstance(FBB.class.getClassLoader(),FBB.class.getInterfaces()
@Override
* proxy: 代理者
* args:挡墙调用的方法的参数数组
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("不好意思,给多少钱不拍了~~");
}else{
Object returnObj = method.invoke(fbb, args);
return returnObj;
}
//从此之后,不允许直接调用被代理者身上的方法,而是要通过代理者来调用
//fbb.唱歌();
proxy.唱歌();
}
JDK的运行图:
java动态代理的特点:
优点:
不需要像静态代理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。
缺点:
java的动态代理是通过代理者实现和被代理者相同的接口来保证两者具有相同的方法的,如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。
所以java的动态代理机制是基于接口进行的,受制于要代理的方法是否有接口的支持。
修改EasyMall项目:
修改controller层代码:
修改代理类
解释:
InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部
* 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
* 参数31:Object proxy :代理对象
* 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
* 执行方法名:method.getName()
* 执行方法:method.invoke(对象,实际参数)
* 参数33:Object[] args :方法实际参数
Serevice的实现类:
第三方包cglib实现的动态代理
CGLIB是第三方提供的动态代理的实现工具,不管有没有接口都可以实现动态代理。
CGLIB实现动态代理的原理是 生成的动态代理是被代理者的子类,所以代理者具有和父类即被代理者 相同的方法,从而实现代理。
步骤1:导入CGLIB相关包
之前的导入的spring包中就包含CGLIB
Spring-core-3.2.3.RELEASE.jar
步骤2:开发CGLIB程序
案例:
package cn.tedu.cglibproxy;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.cglib.proxy.MethodInterceptor;
@Test
final FBB fbb = new FBB();
//增强器
enhancer.setInterfaces(fbb.getClass().getInterfaces());
//设定父类 -- 此处要传入被代理者的类,cglib是通过集成被代理者的类来持有和被代理者相同的方法的,此方法必须设置
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args,
if("拍电影".equals(method.getName())){
return null;
System.out.println("检查权限。。。");
System.out.println("记录日志。。。");
}
});
//生成代理对象
proxy.吃();
proxy.拍电影();
}
CGBLIB的原理图:
CGLIB动态代理的特点:
优点:无论是否有接口都可以实现动态代理,使用场景基本不受限
缺点:第三方提供的动态代理机制,不是原生的,需要导入第三方开发包才可以使用。
修改EasyMall的的代理类:
在service的代理类:
web类: