简述
谈这个首先要明白什么是IOC IOC(IoC Inversion of Control (控制反转/反转控制)),注意它是⼀个技术思想,不是⼀个技术实现
描述的事情: Java开发领域对象的创建,管理的问题
所要实现的事情就是我们可以不自己去new对象来使用 而是通过IOC容器去创建对象然后我们直接去IOC容器中取来使用
说到这里还有一个概念需要提及 DI:Dependancy Injection(依赖注⼊)
IOC和DI其实本质上说的是同一件事情,只不过角度不同,IOC是站在被注入对象的角度上 讲述这个对象被控制反转。而DI是站在容器的角度上 讲述这个容器将对象进行依赖注入。
然后就是AOP
AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程
AOP是OOP的一个延续,或者说延伸
使代码在不改变原有逻辑的情况下 增强横切逻辑代码 根本上解藕 避免横切逻辑代码重复
下面来具体说明下关于Spring的IOC AOP的实现
首先 基于java的三层架构 我们想要在servlet中获取并使用或者service中获取并使用dao 那么我们最简单的方法便是new一个具体的实例来进行操作
但是这样的话 一直的new对象 又无法保证该对象会被按时回收 首先堆中会不断堆积 影响到jvm的具体性能 还有这样的代码耦合性太强 java的特点就是要考虑具体的解耦问题
那么 我们就需要考虑通过其他方式使该对象注入并能够使用
在这个情况下 我们就考虑到通过xml文件的方式去解决
首先通过xml文件将具体的class文件进行定义 确定其路径以及id 之后通过工厂的方式生产其对象
1 <bean> 2 <bean id="AccountDao" class="com/lagou/edu/dao/AccountDao.java"/> 3 </bean>
1 // 实例化bean对象 2 for (int i = 0; i < list.size(); i++) { 3 Element element = list.get(i); 4 String id = element.attributeValue("id"); 5 String clazz = element.attributeValue("class"); 6 Class<?> aClass = Class.forName(clazz); 7 Object o = aClass.newInstance(); 8 map.put(id, o); 9 }
通过工厂类将其实例化同时还要维护他的具体依赖关系 这个可以通过循环来处理他的property元素 之后 我们在将其放在map中以供之后方便使用
这样 我们就可以通过工厂类的方法来创建实力对象 解决代码的耦合问题
但是通过Spring框架我们可以知道这样并不是最简便的
毕竟还要在每一个里面通过BeanFactory.getBean("transferService")获取对象实例
但是直接写 private AccountDao accountDao 得到的实例对象其实是null
我们可以通过set方法来给他赋值 但这样我们首先要将bean对象的值进行下处理 使其set到对应的name中
1 // 维护bean之间的依赖关系 2 List<Element> propertyNodes = 3 rootElement.selectNodes("//property"); 4 for (int i = 0; i < propertyNodes.size(); i++) { 5 Element element = propertyNodes.get(i); 6 // 处理property元素 7 String name = element.attributeValue("name"); 8 String ref = element.attributeValue("ref"); 9 String parentId = 10 element.getParent().attributeValue("id"); 11 Object parentObject = map.get(parentId); 12 Method[] methods = parentObject.getClass().getMethods(); 13 for (int j = 0; j < methods.length; j++) { 14 Method method = methods[j]; 15 if (("set" + name).equalsIgnoreCase(method.getName())) { 16 // bean之间的依赖关系(注⼊bean) 17 Object propertyObject = map.get(ref); 18 method.invoke(parentObject, propertyObject); 19 } 20 } 21 // 维护依赖关系后重新将bean放⼊map中 22 map.put(parentId, parentObject);
这样当我们通过
1 private AccountDao accountDao; 2 3 public void setAccountDao(AccountDao accountDao){ 4 this.accountDao = accountDao; 5 }
的方式才能使accountDao的值不为null
处于实际问题 我们还要考虑关于事务的控制
如何保证事务的控制 那就要保证多次的commit操作使用的是同一个connection连接 方法 可以获取当前线程中的connection
1 public Connection getCurrentThreadConn() throws SQLException { 2 //判断当前线程中是否绑定连接 没有 从连接池获取一个绑定到当前线程 3 Connection connection = threadLocal.get(); 4 if (threadLocal.get() == null) { 5 // connection = DruidUtils.getInstance().getConnection(); 6 //从连接吃那连接并绑定到线程 7 connection = dataSource.getConnection(); 8 //绑定当前线程 9 threadLocal.set(connection); 10 } 11 return connection; 12 }
这样的话 其实就算是基本还原了
不过 如果有大量的涉及到事务的操作时 我们就需要进行大量的重复操作
1 try { 2 TransactionManager.getInstance().beginTransaction(); 3 4 Account from = accountDao.queryAccountByCardNo(fromCardNo); 5 Account to = accountDao.queryAccountByCardNo(toCardNo); 6 from.setMoney(from.getMoney()-money); 7 to.setMoney(to.getMoney()+money); 8 accountDao.updateAccountByCardNo(from); 9 accountDao.updateAccountByCardNo(to); 10 11 TransactionManager.getInstance().commit(); 12 } catch (Exception e){ 13 e.printStackTrace(); 14 TransactionManager.getInstance().rollback(); 15 throw e; 16 }
其中的开启事务 提交事务 包括捕获异常后的事务回滚我们都要大量的重复书写 这里就涉及到了动态代理模式的使用
代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
其中还分为动态代理和静态代理
静态代理总结:
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
相对于静态代理来说 动态代理教完美的解决了这一问题 为什么说较完美呢 因为jdk的动态代理还是摆脱不了对于interface的依赖 对应的就是我们需要创建同数量的接口
这也是很大的工程
1 @CallerSensitive 2 public static Object newProxyInstance(ClassLoader loader, 3 Class<?>[] interfaces, 4 InvocationHandler h) { 5 Objects.requireNonNull(h); 6 7 final Class<?> caller = System.getSecurityManager() == null 8 ? null 9 : Reflection.getCallerClass(); 10 11 /* 12 * Look up or generate the designated proxy class and its constructor. 13 */ 14 Constructor<?> cons = getProxyConstructor(caller, loader, interfaces); 15 16 return newProxyInstance(caller, cons, h); 17 }
于是 更加方便的cjlib动态代理就应需产生了
1 public static Object create(Class type, Callback callback) { 2 Enhancer e = new Enhancer(); 3 e.setSuperclass(type); 4 e.setCallback(callback); 5 return e.create(); 6 }
可以跳过接口直接通过class来代理对象
所以 我们使用动态代理将大量的重复事务操作进行抽取 使代码不再臃肿
1 public Object getCglibProxy(Object obj) { 2 return Enhancer.create(obj.getClass(), new MethodInterceptor() { 3 @Override 4 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 5 Object result = null; 6 try{ 7 transactionManager.beginTransaction(); 8 9 result = method.invoke(obj, objects); 10 11 transactionManager.commit(); 12 }catch (Exception e) { 13 e.printStackTrace(); 14 15 transactionManager.rollback(); 16 17 throw e; 18 } 19 20 return result; 21 } 22 }); 23 }
这样 我们就可以直接书写业务模块了