Spring框架学习一(IOC、DI、AOP思想)

Spring框架学习一(IOC、DI、AOP思想)

1.控制反转IOC和依赖注入DI

​ 首先了解到Spring框架是通过标签来将对象注入到容器中,管理对象的创建过程。这就是控制反转的思想。那么为什么会出现这种思想呢?

​ 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。这么多对象难免会存在耦合关系,类似于手表中的齿轮,各个部件相互依赖,协同工作,完成指针的运转。对象之间的关系就类似于齿轮,紧密相连。如果其中一个齿轮出现问题,整个系统就无法完成工作。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。但是必须得避免对象之间的多重依赖关系,因此出现了控制反转这一思想。

什么叫控制:传统的程序设计中,当某个对象需要另一个对象时,通常是在对象中创建另一对象,而在Spring中将对象的创建放在容器中!就是由spring来负责控制对象的生命周期和对象间的关系。

什么叫反转依赖对象的获取被反转了,之前是主动创建的,而在Spring框架中是容器来控制注入到对象中,对象只是被动的接受依赖对象。

对于DI和IOC其实就是相同概念不同角度的描述

什么叫依赖:应用程序依赖于IOC容器来创建和注入对象所需要的外部资源(对象,常量值…)。

什么叫注入:IOC容器注入外部资源到对象中。

依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

SpringIOC的设计原理

在这里插入图片描述

2.注入的实现和注解

​ 在这需要先导入Spring的jar包和约束文件:

jar

  1. spring-beans:Spring IOC的基础实现,包含访问配置文件、创建和管理bean等。
  2. spring-core:Spring的核心容器
  3. spring-context:在基础IOC功能上提供扩展服务
  4. spring-expression:Spring表达式语言

约束文件:spring-beans.xsd(bean标签)

​ spring-context.xsd(指定扫描哪些包中注解)<context:component-scan base-package=“包名”>

2.1.实例化bean

  1. xml方式

    1. 主要是通过标签
  2. 注解方式

    1. @Component
    2. @Controller
    3. @Service
    4. @Resposity

    这四个注解的作用都是一样的,为了方便区分才这样写!

2.2.bean属性注入

​ Spring的本质就是一个bean工厂或者说是bean容器,它按照我们的要求,生产需要的bean。一般通过两种方式对bean的属性初始化。

  1. set方法注入:

    1. 值类型:<property name="" value=""></>
    2. 引用类型:<property name="" ref=""></>
  2. 构造注入:

    1. 值类型:<constructor name="参数名" value="参数值" index="参数名的索引位置" type="参数名的类型">
    2. 引用类型:value改成ref

    ref的值要和bean标签中的id名一致

  3. 注解方式

    1. 值类型:
      1. 直接在字段上:@Value("")
      2. 在set方法上:@Value("")
    2. 引用类型:
      1. @Autowired:自动装配,不需要指定类型名(当出现类型名相同的时候有可能无法选择,不过一般没有人会这么做)
      2. @Qualifier(“类型名”):和Autowired一起使用指定类型名
      3. Resource(name=“类型名”):手动注入指定类型名

3.spring整合junit测试

​ 一般使用@test测试会比较麻烦,要先读取配置文件在使用getBean()。

ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
User user=(User) ac.getBean("user");

​ Spring中有个spring-text.jar包可以帮助我们方便的进行测试。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
相当于
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
对于User user:
可以直接在上面通过注解的引用类型方式
@Autowired
User user;

4.Spring的AOP思想

​ 通过一个简单的银行例子来说明,银行有两个流程取款和查询余额,很明显发现验证用户这一操作是重复的。

在这里插入图片描述

​ 于是我们可以将验证用户这一操作设置为一个公共方法,这样每个流程就不需要对于验证用户这一操作copy一份了。但是仍然会出现一个问题,每个流程都需要调用公共方法。

在这里插入图片描述

​ 这时联想到spring的依赖注入,似乎可以将验证用户提取出来,当需要的时候就注入流程中。对于提取和注入的流程通过一些专用名词来定义。

在这里插入图片描述

首先将取款流程和查询流程看对是一个一个类。

Aspect切面:相当于Java中的类,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

PointCut切点:需要增强的方法,类似于上例中的验证用户。切点定义了Advice将要发生的地方。

Advice(通知/增强):也是一系列方法,主要是用来修饰PointCut,也就是说可以在验证用户操作之前之后调用这些方法来增强验证用户这一方法。类似于动态代理。

Join Point连接点:首先可以先想象一下一个切面不可能每个方法都是PointCut(需要增强)。joinPoint代表的是可以被增强的方法。

Target目标对象:被通知的对象,上例中的取款和查询流程。

再举一个例子来源

​ 下面我以一个简单的例子来比喻一下 AOP 中 Aspect, Joint point, Pointcut 与 Advice之间的关系.

让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系.
首先我们知道, 在 Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 point cut是一个描述信息, 它修饰的是 Joint point, 通过 point cut, 我们就可以确定哪些 Joint point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, Joint point 就相当于 爪哇的小县城里的百姓,pointcut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问.
为什么可以这样类比呢?

  • Joint point : 爪哇的小县城里的百姓: 因为根据定义, Joint point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.

  • Pointcut :男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 Advice, 但是我们并不希望在所有方法上都织入 Advice, 而 Pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问.

  • Advice :抓过来审问, Advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 Joint point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓.

  • Aspect::Aspect 是 point cut 与 Advice的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 Aspect.

1.Servlet中AOP的体现

​ AOP意思就是面向切面编程,可以用一句话概况AOP编程思想,即横向重复,纵向抽取。在使用servlet编程的时候通常需要解决乱码问题,每个Servlet都需要解决就很麻烦。通常使用filter来一次性解决代码问题。

​ 参照第一个例子,将每个Servlet看成一个纵向流程,解决乱码就是横向重复的(对于每个Servlet都会有这个操作),Filter通过拦截每个Servlet然后定义一个类来解决乱码问题,最后再放行。这就是纵向抽取(将乱码问题抽取出来统一解决)。

2.Spring中的AOP实现

首先导入AOP的jar包和约束文件

jar包

  • spring-aop.jar:aop包
  • spring-aspect.jar:切面包
  • com.springsource.org.aspectj.weaver.RELEASE.jar:织入包
  • com.springsource.org.aopalliance.jar:aop联盟包

约束文件:spring-aop.xsd

实现AOP原理

  1. 动态代理:有关动态代理详解及源码分析AOP主要实现就是通过动态代理,此方式需要被代理的类需要实现接口。
  2. cglib代理:可以对任何类实现代理,主要是对目标对象继承代理(故类不能被final修饰)

applicationContext.xml

<!-- 1.配置目标对象 -->
	<bean name="userService" class="Service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="Advice.MyAdvice" ></bean>
<!-- 3.配置将通知织入目标对象 -->
	<aop:config>
		<aop:pointcut expression="execution(* Service.*ServiceImpl.*(..))" id="pc"/>
		<aop:aspect ref="myAdvice" >
			<!-- 指定名为before方法作为前置通知 -->
			<aop:before method="before" pointcut-ref="pc" />
			<!-- 后置 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pc" />
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="pc" />
			<!-- 异常拦截通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pc"/>
			<!-- 后置 -->
			<aop:after method="after" pointcut-ref="pc"/>
		</aop:aspect>
	</aop:config>
</beans>

UserServiceImpl.java(目标对象)

public class UserServiceImpl implements UserService{
	public void save() {
		System.out.println("保存用户!");
	}
	public void delete() {
		System.out.println("删除用户!");
	}
	public void update() {
		System.out.println("更新用户!");
	}
	public void find() {
		System.out.println("查找用户!");
	}
}

UserService.java

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}

Myadvice.java(通知类)

//通知类
public class MyAdvice {
	public  void before(){
		System.out.println("前置通知!");
	}
	public  void afterReturning(){
		System.out.println("后置通知!(出现异常不会运行)");
	}
	public  Object around(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕通知之前!");
		Object proceed=pjp.proceed();
		System.out.println("环绕通知之后!");
		return proceed;
	}
	public  void afterThrowing(){
		System.out.println("异常拦截通知!");
	}
	public  void after(){
		System.out.println("后置通知!(出现异常也会运行)");
	}
}

AOPDemo.java(测试)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPDemo {
	@Resource(name="userService")
	private UserService us;
	@Test
	public void fun(){
		us.save();
	}
}

参考:https://www.cnblogs.com/xys1228/p/6057587.html?utm_source=itdadao&utm_medium=referral

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章