Java 开发必读,谈谈对 Spring IOC 与 AOP 的理解

Java开发必读,谈谈对Spring IOC与AOP的理解

一、前言

IOC 和 AOP 是 Spring 中的两个核心的概念,下面谈谈对这两个概念的理解。

二、IOC(Inverse of Control)

控制反转,也可以称为依赖倒置。

所谓依赖,从程序的角度看,就是比如 A 要调用 B 的方法,那么 A 就依赖于 B,反正 A 要用到 B,则 A 依赖于 B。所谓倒置,你必须理解如果不倒置,会怎么着,因为 A 必须要有 B,才可以调用 B,如果不倒置,意思就是 A 主动获取 B 的实例:B b = new B(),这就是最简单的获取 B 实例的方法(当然还有各种设计模式可以帮助你去获得 B 的实例,比如工厂、Locator 等等),然后你就可以调用 b 对象了。所以,不倒置,意味着 A 要主动获取 B,才能使用 B;到了这里,就应该明白了倒置的意思了。倒置就是 A 要调用 B 的话,A 并不需要主动获取 B,而是由其它人自动将 B 送上门来。

形象的举例就是:

通常情况下,假如你有一天在家里口渴了,要喝水,那么你可以到你小区的小卖部去,告诉他们,你需要一瓶水,然后小卖部给你一瓶水!这本来没有太大问题,关键是如果小卖部很远,那么你必须知道:从你家如何到小卖部;小卖部里是否有你需要的水;你还要考虑是否开着车去;等等等等,也许有太多的问题要考虑了。也就是说,为了一瓶水,你还可能需要依赖于车等等这些交通工具或别的工具,问题是不是变得复杂了?那么如何解决这个问题呢?

解决这个问题的方法很简单:小卖部提供送货上门服务,凡是小卖部的会员,你只要告知小卖部你需要什么,小卖部将主动把货物给你送上门来!这样一来,你只需要做两件事情,你就可以活得更加轻松自在:

第一:向小卖部注册为会员。

第二:告诉小卖部你需要什么。

这和 Spring 的做法很类似!Spring 就是小卖部,你就是 A 对象,水就是 B 对象

第一:在 Spring 中声明一个类:A

第二:告诉 Spring,A 需要 B

假设 A 是 UserAction 类,而 B 是 UserService 类。

<bean id="userService" class="org.leadfar.service.UserService"/>  <bean id="documentService" class="org.leadfar.service.DocumentService"/>  <bean id="orgService" class="org.leadfar.service.OrgService"/>    <bean id="userAction" class="org.leadfar.web.UserAction">       <property name="userService" ref="userService"/>  </bean>  

复制代码

在 Spring 这个商店(工厂)中,有很多对象/服务:userService,documentService,orgService,也有很多会员:userAction 等等,声明 userAction 需要 userService 即可,Spring 将通过你给它提供的通道主动把 userService 送上门来,因此 UserAction 的代码示例类似如下所示:

package org.leadfar.web;  public class UserAction{       private UserService userService;       public String login(){            userService.valifyUser(xxx);       }       public void setUserService(UserService userService){            this.userService = userService;       }  }  

复制代码

在这段代码里面,你无需自己创建 UserService 对象(Spring 作为背后无形的手,把 UserService 对象通过你定义的 setUserService()方法把它主动送给了你,这就叫依赖注入!),当然咯,我们也可以使用注解来注入。Spring 依赖注入的实现技术是:动态代理

三、AOP:面向切面编程

面向切面编程的目标就是分离关注点。什么是关注点呢?就是你要做的事,就是关注点。假如你是个公子哥,没啥人生目标,天天就是衣来伸手,饭来张口,整天只知道玩一件事!那么,每天你一睁眼,就光想着吃完饭就去玩(你必须要做的事),但是在玩之前,你还需要穿衣服、穿鞋子、叠好被子、做饭等等等等事情,这些事情就是你的关注点,但是你只想吃饭然后玩,那么怎么办呢?这些事情通通交给别人去干。在你走到饭桌之前,有一个专门的仆人 A 帮你穿衣服,仆人 B 帮你穿鞋子,仆人 C 帮你叠好被子,仆人 D 帮你做饭,然后你就开始吃饭、去玩(这就是你一天的正事),你干完你的正事之后,回来,然后一系列仆人又开始帮你干这个干那个,然后一天就结束了!

AOP 的好处就是你只需要干你的正事,其它事情别人帮你干。也许有一天,你想裸奔,不想穿衣服,那么你把仆人 A 解雇就是了!也许有一天,出门之前你还想带点钱,那么你再雇一个仆人 E 专门帮你干取钱的活!这就是 AOP。每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。

从 Spring 的角度看,AOP 最大的用途就在于提供了事务管理的能力。事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring 在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!

我们在使用 Spring 框架的过程中,其实就是为了使用 IOC(依赖注入)和 AOP(面向切面编程),这两个是 Spring 的灵魂。主要用到的设计模式有工厂模式代理模式。IOC 就是典型的工厂模式,通过 sessionfactory 去注入实例;AOP 就是典型的代理模式的体现。

代理模式是常用的 java 设计模式,他的特征是代理类委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类委托类之间通常会存在关联关系,一个代理类对象与一个委托类对象关联,代理类对象本身并不真正实现服务,而是通过调用委托类的对象相关方法,来提供特定的服务。

Spring IoC 容器是 spring 的核心,spring AOP 是 spring 框架的重要组成部分。

在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在 spring 里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通常由 spring 容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入控制反转是同一个概念。

面向方面编程(AOP)是从另一个角度来考虑程序结构,通过分析程序结构的关注点来完善面向对象编程(OOP)。OOP 将应用程序分解成各个层次的对象,而 AOP 将程序分解成多个切面。spring AOP 只实现了方法级别的连接点,在 J2EE 应用中,AOP 拦截到方法级别的操作就已经足够。在 spring 中为了使 IoC 方便地使用健壮、灵活的企业服务,需要利用 spring AOP 实现为 IoC 和企业服务之间建立联系。

IOC:控制反转也叫依赖注入。利用了工厂模式。

将对象交给容器管理,你只需要在 spring 配置文件中配置相应的 bean,以及设置相关的属性,让 spring 容器来生成类的实例对象以及管理对象。在 spring 容器启动的时候,spring 会把你在配置文件中配置的 bean 都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些 bean 分配给你需要调用这些 bean 的类(假设这个类名是 A),分配的方法就是调用 A 的 setter 方法来注入,而不需要你在 A 里面 new 这些 bean 了。

注意:面试的时候,如果有条件,画图,这样更加显得你懂了。

四、AOP(Aspect-Oriented Programming):面向切面编程补充说明

AOP 可以说是对 OOP 的补充和完善。OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP 则显得无能为力。也就是说,OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。

将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。

实现 AOP 的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

简单点解释,比方说你想在你的 biz 层所有类中都加上一个打印‘你好’的功能,这时就可以用 aop 思想来做.你先写个类写个类方法,方法经实现打印‘你好’,然后 Ioc 这个类 ref=“biz.*”让每个类都注入即可实现。

五、Spring 中对 AOP 的支持

Spring 中 AOP 代理由 Spring IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他 Bean 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。Spring 默认使用 Java 动态代理来创建 AOP 代理,这样就可以为任何接口实例创建代理了。当需要代理的类不是代理接口的时候,Spring 自动会切换为使用 CGLIB 代理,也可强制使用 CGLIB。

5.1 程序员参与部分

AOP 编程其实是很简单的事情。纵观 AOP 编程,其中需要程序员参与的只有三个部分:

  1. 定义普通业务组件。

  2. 定义切入点,一个切入点可能横切多个业务组件。

  3. 定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。

所以进行 AOP 编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP 框架将会自动生成 AOP 代理,即:代理对象的方法 =增强处理 +被代理对象的方法

5.2 Spring 中使用方式

基于 Annotation 的“零配置”方式。

(1)启动注解,配置文件 applicationContext.xml

<!-- 启动对@AspectJ注解的支持 -->       <aop:aspectj-autoproxy/>  <bean id="user" class="com.tgb.spring.aop.IUserImpl"/>  <bean id="check" class="com.tgb.spring.aop.CheckUser"/>  

复制代码

(2)编写切面类

package com.tgb.spring.aop;  import org.aspectj.lang.ProceedingJoinPoint;  import org.aspectj.lang.annotation.After;  import org.aspectj.lang.annotation.Around;  import org.aspectj.lang.annotation.Aspect;  import org.aspectj.lang.annotation.Before;  import org.aspectj.lang.annotation.Pointcut;    @Aspect   public class CheckUser {      @Pointcut("execution(* com.tgb.spring.aop.*.find*(..))")      public void checkUser(){        System.out.println("The System is Searching Information For You");      }         @Pointcut("execution(* com.tgb.spring.aop.*.add*(..))")      public void checkAdd(){          System.out.println("<< Add User >> Checking.....");      }            @Before("checkUser()")      public void beforeCheck(){          System.out.println(">>>>>>>> 准备搜查用户..........");      }            @After("checkUser()")      public void afterCheck(){          System.out.println(">>>>>>>> 搜查用户完毕..........");      }        @Before("checkAdd()")      public void beforeAdd(){          System.out.println(">>>>>>>> 增加用户--检查ing..........");      }            @After("checkAdd()")      public void afterAdd(){          System.out.println(">>> 增加用户--检查完毕!未发现异常!........");      }        //声明环绕通知        @Around("checkUser()")        public Object doAround(ProceedingJoinPoint pjp) throws Throwable {            System.out.println("进入方法---环绕通知");            Object o = pjp.proceed();            System.out.println("退出方法---环绕通知");            return o;        } }  

复制代码

(3)定义接口

package com.tgb.spring.aop;  public interface IUser {      public String findUser(String username);      public void addUser(String username);      public void findAll();  }

复制代码

(4)定义实现

package com.tgb.spring.aop;  import java.util.HashMap;  import java.util.Map;  public class IUserImpl implements IUser {      public static Map map = null;      public static void init(){          String[] list = {"Lucy", "Tom", "小明", "Smith", "Hello"};          Map tmp = new HashMap();          for(int i=0; i<list.length; i++){              tmp.put(list[i], list[i]+"00");          }          map = tmp;      }
    public void addUser(String username) {          init();          map.put(username, username+"11");          System.out.println("---【addUser】: "+username+" --------");          System.out.println("【The new List:"+map+"】");      }        public void findAll() {          init();          System.out.println("---------------【findAll】: "+map+" ------------------");      }        public String findUser(String username) {          init();          String password = "没查到该用户";          if(map.containsKey(username)){              password = map.get(username).toString();          }          System.out.println("-----------------【findUser】-----------------");          System.out.println("-----------Username:"+username+"------------");          System.out.println("-----【Result】:"+password+"--------");          return password;      }  }  

复制代码

(5)测试

public class Test {      public static void main(String as[]){          BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");          IUser user = (IUser) factory.getBean("user");          user.findAll();          User u = new User();  //      u.setUsername("Tom");  //      user.findUser(u.getUsername());          /*u.setUsername("haha");         user.addUser(u.getUsername());*/      }  }

复制代码

注:@Before 是在所拦截方法执行之前执行一段逻辑。@After 是在所拦截方法执行之后执行一段逻辑。@Around 是可以同时在所拦截方法的前后执行一段逻辑。

以上是针对注解的方式来实现,那么配置文件也一样,只需要在 applicationContext.xml 中添加如下代码:

<!--  <aop:config>          <aop:pointcut id="find" expression="execution(* com.tgb.spring.aop.*.find*(..))" />          <aop:pointcut id="add"   expression="execution(* com.tgb.spring.aop.*.add*(..))" />          <aop:aspect id="checkUser" ref="check">              <aop:before method="beforeCheck" pointcut-ref="find"/>              <aop:after method="afterCheck" pointcut-ref="find"/>          </aop:aspect>                    <aop:aspect id="checkAdd" ref="check">              <aop:before method="beforeAdd" pointcut-ref="add"/>              <aop:after method="afterAdd" pointcut-ref="add"/>          </aop:aspect>  </aop:config>--> 

复制代码

```python
class BertPooler(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.dense = nn.Linear(config.hidden_size, config.hidden_size)
        self.activation = nn.Tanh()

    def forward(self, hidden_states):
        # We "pool" the model by simply taking the hidden state corresponding
        # to the first token.
        first_token_tensor = hidden_states[:, 0]
        pooled_output = self.dense(first_token_tensor)
        pooled_output = self.activation(pooled_output)
        return pooled_output
from transformers.models.bert.configuration_bert import *
import torch
config = BertConfig.from_pretrained("bert-base-uncased")
bert_pooler = BertPooler(config=config)
print("input to bert pooler size: {}".format(config.hidden_size))
batch_size = 1
seq_len = 2
hidden_size = 768
x = torch.rand(batch_size, seq_len, hidden_size)
y = bert_pooler(x)
print(y.size())
```
  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值