SSM框架梳理(二)Spring原理解析

154 阅读3分钟

一.Spring

上一篇文章描述了SpringMVC的运行流程,本篇文章开始讲Spring。我们已经知道,在SSM里,系统分为了表现层、service层、controller层、DAO层。那么Spring是干什么的,在SSM里又扮演了哪些角色,在这四个层里的哪些层发挥了作用呢?

1.Bean

了解Spring之前,先讲一下Bean。

我们都知道JAVA是面向对象的语言,也就是类,对象有方法和属性,在调用对象的时候需要先实例化对象,然后来调用对象里的方法和属性,这是java开发的基本原则之一。不实例化,是没有办法使用对象的内容的。Bean就是把这些类都注册起来,注册的方式是给这些类加上注解,把他们注册到Spring IOC。Bean可以理解为类的代理人(通过java反射实现),通过成为类的代理人,就能够代表类,拥有该类的东西。

那什么是注解,在Spring里注解有以下几种:

@Component , @Repository , @ Controller , @Service , @Configration

都是@开头的,有没有让大家想起微博,在微博上如果我们发一个博客,@某某,那么这个人就会收到通知,优先去查看你的这一条博客,并且回复你。在Spring中是一个道理,你使用了@这个符号,就代表这是一个注解,Spring扫描出来就会优先过来看,它会先把这个类注册成为一个bean,或者给你一个bean来使用。

为什么是注册或者使用呢,因为注解本身就分为两类:①注册类②实用类

实用类:这一类的注解就是把已经在XML里配置好的bean拿出来用,完成属性、方法的组装,有以下两种:

@Autowired , @Resource

注册类:这类注解就是把你要实例化的对象转化为一个bean,放到IOC容器里,跟上面的@Autowired , @Resource配合使用,有以下几种:

@Component , @Repository , @Controller , @Service , @Configration

2.IOC

我们在讲bean的时候多次提到,要将bean注册到ioc里,这个ioc是什么呢?

首先我们来看一句话,这是关于Spring官方给的一句话:Spring是一个轻量级的控制反转和面向切片的容器框架。这里面的控制反转就是IOC,面向切片就是AOP。

IOC控制反转也叫做依赖注入。利用了工厂模式(工厂提货,不用管生产线)把对象交给了容器来管理,这个容器就是Spring,所以我们往往也把Spring叫做Bean的大工厂。在使用时,我们只需要在spring配置文件中配置相应的bean,设置相关的属性,就可以让spring容器来生成类的实例化对象以及管理对象。

听不明白的话我们换一下这句话,所谓的控制反转,就是把创建对象(bean)和维护对象(bean)之间的关系权利从程序转移到spring容器(spring-context.xml)中。

以下是我自己的项目中,将redis缓存类放在xml里配置为bean,方便后续使用的代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.3.xsd"
    default-lazy-init="true">
    <description>Jedis Configuration</description>

    <!-- 加载配置文件 -->  
    <context:property-placeholder ignore-unresolvable="true" location="classpath:config.properties" />  
    
    <!-- redis连接池配置-->    
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >    
        <!--最大空闲数-->    
        <property name="maxIdle" value="${redis.maxIdle}" />    
        <!--连接池的最大数据库连接数  -->  
        <property name="maxTotal" value="${redis.maxTotal}" />  
        <!--最大建立连接等待时间-->    
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />    
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)-->  
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />   
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3-->  
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />   
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1-->  
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />   
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->    
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />    
        <!--在空闲时检查有效性, 默认false  -->  
        <property name="testWhileIdle" value="${redis.testWhileIdle}" />    
    </bean >  
    <!--自定义redis工具类,在需要缓存的地方注入此类  -->  
    <bean id="jedisUtils" class="com.xxx.lrt.frame.core.utils.JedisUtils">  
        <property name="redisTemplate" ref="redisTemplate" />  
    </bean>    

可以看到,bean有id,有class文件的指向,bean内部需要给property属性,每一个属性都有名称和值。

再来看以下,当我需要使用这个bean的时候,代码是这么写的:

 @Autowired
    @Qualifier(value = "jedisUtils")
    private JedisUtils            jedisUtils;

可以看到,使用了@Autowired这个注解,这个注解是干嘛的,前面提到过,是装载bean,把bean拿来使用的。顺便解释一下这个@Qualifier注解,这个@Qualifier是指向jedisUtils这个bean名称,要不然spring不知道是要去装载哪一个bean,比如我上面贴的xml代码里,除了jedisUtils还有一个id为jedisPoolConfig的bean,他们两个是相同类型的,这个时候如果不指定一个name,spring无法判断它需要去装载的是哪一个bean,就会抛一个NoSuchBeanDefinitionException的异常。

大家再看一下,我controller里面调用service层,以及service层调用dao层的时候代码:

@Controller
@RequestMapping(value = "/CheckOrderApp")
public class CheckOrderAppController extends BaseController {
    @Autowired
    private CheckOrderAppService  checkorderAppService;

    @Autowired
    private RepairOrderAppService repairOrderAppService;;
}

@Service
@Transactional(readOnly = false)
public class CheckOrderAppServiceImpl extends BaseService implements CheckOrderAppService {
    @Autowired
    private CheckOrderAppDao checkorderDao;
}

@Controller的是控制层,@Service的是实现层,他们分别装载了service和dao的bean,这里就只用了@Autowired而没有用@Qualifier指定一个名称,为啥呢?因为我那个CheckOrderAppService这个文件里就只有一个service,RepairOrderAppService也是一个单独的service,spring不存在找不到的问题,同理CheckOrderAppDao也是一个独立的bean,下面是CheckOrderAppDao的注册bean代码:

@MyBatisDao
public interface CheckOrderAppDao {
}

这一块就解释清楚了,当你找寻的xml里有不止一个同类型的bean时,装载时需要指定bean名称,当只有一个bean时,不需要指定。

以上就是IOC控制反转。

3.AOP

如果说IOC代表了spring的工作模式,那么AOP就只是spring的一个特性,AOP(面向切片)跟OOP(面向对象)一样都是一种编程思想,不是一种技术或者工具。我们都知道OOP面向对象,核心是什么?核心就是封装、集成、多态,接口重用是面向对象的灵魂,世间万物都是对象,都可以把它封装起来,然后去花式调用。这样的思想给应用编程带来了很大的便利,但是也不是万能的。在OOP的众多缺点(其实我感觉就两个,一个是资源耗费多,一个就是切面处理)中,尤为突出的一个,就是横切关注点。

我想了半天才想到这么一个例子:

一个人,要去打酱油,按照OOP我们对人这个对象进行抽象,首先是属性:性别、年龄、出发点、出发时间等等,然后是方法(动作):去打酱油、打酱油、打完酱油回家。这是面向对象,我把这个人给抽象了。他最终就是去打酱油,打完酱油回来了,就是要干这么多事。抽象完你只需要告诉你的对象,你去打酱油,打完回家。如果你在去打酱油的路上,下雨了,路太滑,摔了一跤,摔跤这个动作,影响你的主业务(打酱油)吗?不影响,爬起来继续去打酱油,打满。你回来的路上,又摔了一跤,这个摔跤的动作,依旧不影响你打完酱油回家的主业务,但是你会因为摔跤,把瓶子里的酱油洒了一些,导致你瓶子没打满。

这里我们可以看出来,OOP只关心你去打酱油和回来,它不会去管你去的路上和回来的路上有没有遭重,因为这个业务对打酱油本身不影响。这个在你去和回来的路上可能遇到的事,就是我们面向切片AOP写的自定义方法,不会对OOP主业务造成影响,但是我可以操纵这个方法使得你最终的结果产生变化。

总结一下,OOP对业务分析中抽取的实体进行属性和方法的封装,强调了代码的复用性,AOP偏重业务处理过程的某个步骤,强调降低模块之间的耦合度,使代码拥有更好的移植性。OOP面向业务中的名词领域,而AOP面向的是动词领域。

AOP解剖了OOP封装的对象,将那些赢了多个类的公共行为封装到一个可重用的模块,命名为“Aspect”,即方面 。简单来说,就是将那些与业务无关,却又为业务模块所共同调用的逻辑或这人封装起来,减少代码重复,降低模块间的耦合度,有利于未来的可操作性和可维护性。

AOP可以避免OOP的大规模代码重复,用于处理程序中的交叉业务逻辑:权限认证、日志、事务处理

想想看,一个大西瓜是OOP封装的对象,又大又圆。你用AOP把西瓜横切了,得到一个横切面,发现里面好多西瓜籽,你就把西瓜子都提取出来,最神奇的是这一切干完了之后你还能把这个西瓜复原回去,这就是AOP牛皮的地方了。

spring里所谓动态代理AOP(proxy),就是截取消息的方式,对该内容进行装饰,以取代原有对象行为的执行。

AOP使用场景包含但不仅限于以下:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging  调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence  持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务
————————————————
版权声明:本文为CSDN博主「MoreeVan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:blog.csdn.net/moreevan/ar…

好了,讲到这里,我们对spring的一些特性和运作方式已经有了基本的了解,包含了 Bean工厂、IOC控制反转以及bean的注册和装载、AOP和OOP的区别,AOP的使用场景等,下一篇我会简单地介绍下SSM的第三个子框架,Mybatis,然后就从项目去解剖SSM的具体应用。