1. 分析程序中存在的问题(高内聚,低耦合)
通过springIOC DI) 以及注解的使用,成功解决了在程序中层与层之间出现的耦合的问题,但是在很多地方仍然存在非该层应该实现的功能,造成了 无法“高内聚”的现象,同时存在大量存在重复代码,开发效率低下。
1 @Service 2 public class UserServiceImpl implements UserService { 3 @Autowired 4 private UserDao userDao; 5 6 @Override 7 public void registUser(User user) { 8 try { 9 System.out.println("校验权限。。。"); 10 System.out.println("开启事务。。。"); 11 System.out.println("记录日志。。。"); 12 userDao.addUser(user); 13 System.out.println("提交事务。。。"); 14 } catch (Exception e) { 15 System.out.println("回滚事务"); 16 e.printStackTrace(); 17 } 18 } 19 20 @Override 21 public void upToVIP(User user) { 22 try { 23 System.out.println("校验权限。。。"); 24 System.out.println("开启事务。。。"); 25 System.out.println("记录日志。。。"); 26 userDao.updateUser(user); 27 System.out.println("提交事务。。。"); 28 } catch (Exception e) { 29 System.out.println("回滚事务"); 30 e.printStackTrace(); 31 } 32 33 } 34 35 @Override 36 public void removeUser(User user) { 37 try { 38 System.out.println("校验权限。。。"); 39 System.out.println("开启事务。。。"); 40 System.out.println("记录日志。。。"); 41 userDao.deleteUser(user.getId()); 42 System.out.println("提交事务。。。"); 43 } catch (Exception e) { 44 System.out.println("回滚事务"); 45 e.printStackTrace(); 46 } 47 } 48 49 } 50 此时可以通过代理设计模式,将这部分代码提取到代理者中,简化层中的代码。 51 52 2. 静态代理模式 53 package cn.tedu.staticproxy; 54 public interface SJSkill { 55 public void 吃(); 56 public void 唱歌(); 57 } 58 59 package cn.tedu.staticproxy; 60 public class FBB implements SJSkill{ 61 public void 吃(){ 62 System.out.println("fbb吃饭。。。"); 63 } 64 public void 唱歌(){ 65 System.out.println("fbb唱歌。。。"); 66 } 67 } 68 69 package cn.tedu.staticproxy; 70 public class JJRStaticProxy implements SJSkill{ 71 72 private FBB fbb = new FBB(); 73 74 @Override 75 public void 吃() { 76 System.out.println("权限认证:你谁啊????"); 77 fbb.吃(); 78 System.out.println("记录日志:等我,我记一下来访记录"); 79 } 80 81 @Override 82 public void 唱歌() { 83 System.out.println("权限认证:你谁啊????"); 84 fbb.唱歌(); 85 System.out.println("记录日志:等我,我记一下来访记录"); 86 } 87 88 } 89 90 package cn.tedu.staticproxy; 91 import org.junit.Test; 92 public class StaticProxyTest { 93 @Test 94 public void test01(){ 95 JJRStaticProxy jjr = new JJRStaticProxy(); 96 jjr.吃(); 97 jjr.唱歌(); 98 } 99 }
静态代理设计模式特点:
优点:
结构清晰 易于理解
缺点:
如果被代理者有多个方法,则代理者也需要开发多个方法,其中往往存在大量重复代码,仍然存在代码重复。
静态代理设计模式解决了软件分层过程中 额外的功能代码侵入模块的问题,将额外的功能代码提取到了代理者中进行,但是静态代理实现的代理者中存在大量重复的代码,并没有解决代码重复问题。所以在真正开发中--包括spring的底层,基本不会使用静态代理。
3. 动态代理 - jdk内置的动态代理
在jdk中提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者,并且可以通过内置的回调函数指定代理在工作时的执行逻辑,从而实现基于jdk原生api的动态代理机制。
java.lang.reflect
类 Proxy
java.lang.Object
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
案例:
1 package cn.tedu.javaproxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 import org.junit.Test; 8 9 public class JavaProxyTest { 10 @Test 11 public void test01(){ 12 13 //被代理者 14 final FBB fbb = new FBB(); 15 16 //java动态代理方式 生成fbb的代理者 17 /** 18 * classLoader:用来生成代理者类的类加载器,通常可以传入被代理者类的类加载器 19 * interfaces: 要求生成的代理者实现的接口们,通常就是实现和被代理者相同的接口,保证具有和被代理者相同的方法 20 * invocationHandler: 用来设定回调函数的回调接口,使用者需要写一个类实现此接口,从而实现其中的invoke方法, 21 * 在其中编写代码处理代理者调用方法时的回调过程,通常在这里调用真正对象身上的方法,并且在方法之前或之后做额外操作。 22 */ 23 SJSkill proxy = (SJSkill) Proxy.newProxyInstance(FBB.class.getClassLoader(),FBB.class.getInterfaces() 24 ,new InvocationHandler() { 25 @Override 26 /** 27 * proxy: 代理者 28 * method:当前调用的方法对象 由被代理的方法调用 29 * args:挡墙调用的方法的参数数组 方法调用的参数 30 */ 31 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { 32 if("拍电影".equals(method.getName())){ 33 System.out.println("不好意思,给多少钱不拍了~~"); 34 return null; 35 }else{ 36 System.out.println("检验权限。。。。"); 37 Object returnObj = method.invoke(fbb, args); 38 System.out.println("记录日志。。。。"); 39 return returnObj; 40 } 41 } 42 }); 43 //从此之后,不允许直接调用被代理者身上的方法,而是要通过代理者来调用 44 //fbb.吃(); 45 //fbb.唱歌(); 46 proxy.吃(); 47 proxy.唱歌(); 48 proxy.拍电影(); 49 } 50 }
java动态代理的原理图:
java动态代理的特点:
优点:
不需要像静态代理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。
缺点:
java的动态代理是通过代理者实现和被代理者相同的接口来保证两者具有相同的方法的,如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。
所以java的动态代理机制是基于接口进行的,受制于要代理的方法是否有接口的支持。
4. 动态代理 - 第三方包cglib实现的动态代理
CGLIB是第三方提供的动态代理的实现工具,不管有没有接口都可以实现动态代理。
CGLIB实现动态代理的原理是 生成的动态代理是被代理者的子类,所以代理者具有和父类即被代理者 相同的方法,从而实现代理。
a. 导入CGLIB相关包
之前导入的spring包中就包含了CGLIB
spring-core-3.2.3.RELEASE.jar
b. 开发CGLIB程序
案例:
1 package cn.tedu.cglibproxy; 2 import java.lang.reflect.Method; 3 import org.junit.Test; 4 import org.springframework.cglib.proxy.Enhancer; 5 import org.springframework.cglib.proxy.MethodInterceptor; 6 import org.springframework.cglib.proxy.MethodProxy; 7 8 public class CglibProxyTest { 9 @Test 10 public void test01(){ 11 final FBB fbb = new FBB(); 12 13 //增强器 14 Enhancer enhancer = new Enhancer(); 15 16 //设定接口 -- 此方法要求生成的动态代理额外实现指定接口们 ,单cglib动态代理不是靠接口实现的,所以可以不设置 17 enhancer.setInterfaces(fbb.getClass().getInterfaces()); 18 19 //设定父类 -- 此处要传入被代理者的类,cglib是通过集成被代理者的类来持有和被代理者相同的方法的,此方法必须设置 20 enhancer.setSuperclass(fbb.getClass()); 21 22 //设定回调函数 -- 为增强器设定回调函数,之后通过增强器生成的代理对象调用任何方法都会走到此回调函数中,实现调用真正被代理对象的方法的效果 23 enhancer.setCallback(new MethodInterceptor() { 24 @Override 25 public Object intercept(Object proxy, Method method, Object[] args, 26 MethodProxy methodProxy) throws Throwable { 27 if("拍电影".equals(method.getName())){ 28 System.out.println("对不起,不拍了~~~"); 29 return null; 30 }else{ 31 System.out.println("检查权限。。。"); 32 Object returnObj = method.invoke(fbb, args); 33 System.out.println("记录日志。。。"); 34 return returnObj; 35 } 36 } 37 }); 38 39 //生成代理对象 40 FBB proxy = (FBB) enhancer.create(); 41 proxy.吃(); 42 proxy.唱歌(); 43 proxy.拍电影(); 44 } 45 } 46
CGLIB动态代理原理图:
CGLIB动态代理的特点:
优点:无论是否有接口都可以实现动态代理,使用场景基本不受限
缺点:第三方提供的动态代理机制,不是原生的,需要导入第三方开发包才可以使用。