Spring
的两大核心特性IoC和AoP, IoC(Inersion of Control),即控制反转;AoP(Aspact-OrientedProgramming),即面向切面编程
spring的优点:
- 降低了组件质检的耦合性,实现软件各层之间的解耦
- 可以提供更多服务,如事务处理,消息服务等
- 提供单例模式支持
- 提供了AoP技术,容易实现权限拦截
- 提供了众多辅助类,加快应用程序的开发
- 对主流框架提供了集成支持
- 独立于各种应用服务器
IoC(控制反转)
依赖注入DI(Dependency Injection)和控制反转IoC(Inversion of Control)是同一个概念,当某个对象需要另外一个对象协助时,在传统的设计g过程中,这些工作是调用者创建被调用者,但在Spring中,创建被调用者实例的工作不再由调用者来完成,此称为控制反转,而是通过Spring来完成,然后注入调用者,因此被称为依赖注入
通俗易懂的例子来阐述IoC
某一天小明生病了,但是不清楚自己到底得了什么病,就知道一些症状,这个时候自己决定去药店买药,药店很多种药,小明选择了其中一种药,付钱吃药,希望早点好起来。但是这个过程对病人来说太辛苦了,需要根据说明书,然后自己买药,这个时候想到了直接去看医生,医生做了检查,知道了症状以及病因,短短的几分钟,医生对症下药就能治好,省时又省力
在实例中医生充当了IoC
的作用,根据症状病因,对症下药。小明是对象
,药品就是所需要的外部资源
。通过了医生,小明不用自己去找药品,而是通过医生给药品
从几个方面来理解IoC
问题一、参与者
- 对象
- IoC/DI容器
- 某个对象的外部资源
问题二、依赖关系及为什么需要依赖
对象依赖IoC容器,对象需要IoC容器来提供对象需要的外部资源
问题三、注入对象和内容
IoC容器注入对象,注入对象所需的外部资源
问题四、控制反转
IoC容器控制对象,主要是控制对象实例的创建,反转是相对于正向而言,例如A要使用C,首先是A直接去创建C的对象,A类主动去获取所需要的外部资源C,这种情况称为正向,反转:就是A不再主动获取C,而是被动等待,等待IoC容器获取一个C的实例,然后再反向注入 A中
问题五、控制反转和依赖注入
依赖注入和控制反转是对同一件事情的不同描述;依赖注入是从应用程序的角度描述,应用程序依赖容器创建并注入它所需要的外部资源;控制反转是从容器的角度描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源
依赖注入的三种方式:
- 接口注入
- Constructor注入
- setter注入
三种方式比较
接口注入
具备入侵性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限
Setter注入
对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。
如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。
如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用
构造器注入
在构造期间完成一个完整的、合法的对象,所有依赖关系在构造函数中集中呈现,依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对不变的稳定状态,只有组件的创建者关心其内部的依赖关系
依赖注入
定义一个Car接口
public interface Car {
public void run();
}
定义一个AudiCar类实现Car接口
public class AudiCar implements Car{
private String name; // 汽车系列名称
@Override
public void run() {
System.out.println("奥迪" + name + "... is running");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类:
public class TestIoC {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ioc/applicationContext-ioc.xml");
Car car = (Car)applicationContext.getBean("audiCar");
car.run();
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="audiCar" class="ssm.ioc.AudiCar">
<property name="name" value="A6"/>
</bean>
</beans>
运行结果:
奥迪A6... is running
Process finished with exit code 0
IoC
IoC容器负责接纳bean,并对bean进行管理。在Spring中,BeanFactory
是IoC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖.BeanFactory
提供了配制框架及基本功能,而ApplicationContext
则增加了更多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。
BeanFactory和FactoryBean的区别
BeanFactory是加载的容器,加载一些bean,而FactoryBean用于创建代理类
AoP(面向切面编程)
面向切面编程完善Spring的依赖注入,面向切面编程的目标就是分离关注点,面向切面编程(AoP)是对面向对象编程(OoP)的补充,面向对象编程将程序分解成各个层次的对象,面向切面编程将程序分解成各个切面,各步骤之间有良好的隔离性、源代码无关性
基本概念
- 切面(aspect):通知和切入点共同组成了切面:时间、地点和要发生的故事
- 通知(advice):切面必须要完成的工作,何时要执行这个工作
- 目标(target):被通知的对象,在没有AoP之前,将不能只关注自己要做的事情
- 代理(proxy):向目标对象应用通知后创建的对象
- 连接点(joinpoint):目标对象的程序执行的某个特定位置,例如方法被调用、异常被抛出
- 切点(pointcut):通知定义了切面要发生的故事和时间,切点定义了故事的发生地点,例如方法名称 1.使用正则表达式 2.使用AspectJ表达式
- 引入(Introduction):允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
通俗易懂的例子来阐述AoP
假如你是个公子哥,没啥人生目标,天天就是衣来伸手,饭来张口,整天就知道玩这一件事,每天一睁眼就是想去玩(必须做的事情),但是在玩之前,需要穿衣服、穿鞋子、叠被子等等事情,这些事情就是你的关注点,但是你只想吃饭之后然后玩,怎么办?这些事情交给仆人去做,在走到饭桌之前,有仆人给你穿衣服、鞋子、叠被子,然后你就开始吃饭去玩(一天的正事),然后回来之后又一系列的仆人开始帮你干活。
上述除关注点(吃饭和玩)之外,其它的每件事情都是一个点,这些点构成了一个面,这些事情由仆人(AoP)处理,AoP的好处就是你只需干你的正事(自身的业务代码实现),其它事情(事务管控相关内容)别人(AoP)帮你干,各人各司其职,灵活组合,达到一种可配置的,可插拔的程序结构,优点:简化了代码内容,将目标对象复杂的内容进行解耦,分离业务逻辑和横切关注点
4种实现AoP的方式
- 经典的基于代理AoP
- @AspectJ注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切面
方式一、基于代理的AoP
Spring支持五种类型的通知
- Before(前) org.apringframework.aop.MethodBeforeAdvice
- After-returning(返回后) org.springframework.aop.AfterReturningAdvice
- After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
- Arround(周围) org.aopaliance.intercept.MethodInterceptor
- Introduction(引入) org.springframework.aop.IntroductionInterceptor
以咱们每天都会重复去做的事情睡觉来演示面向切面编程
定义一个接口:Sleep
public interface Sleep {
void sleep(); // 睡觉
}
睡觉是主要的事情,也就是关注点,但是除了睡觉之外,还有一些类似脱衣服等操作,如果把这些代码全加入到sleep当中,就违反了单一原则,这时候需要使用AoP,编写一个SleepHelper类,从类名就能猜到这是一个辅助类,用于处理睡觉之外的事情,用AoP属于就是说它应该是通知
public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("起床后......穿衣服");
}
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("睡觉前.......脱衣服");
}
}
然后在applicationContext.xml中进行配置
<bean id="sleepHelper" class="ssm.aop.SleepHelper"/>
<bean id="human" class="ssm.aop.Human"/>
创建通知的工作已经完成,接下来第二步就是进行相关配置,比较复杂
首先配置一个切点,常用的两种方法:1.使用正则表达式 2.使用AspectJ表达式
配置切点使用正则表达式,匹配了所有的sleep()
<!-- Spring使用org.springframework.aop.support.JdkRegexpMethodPointcut来定义正则表达式切点 -->
<bean id="sleepPointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*sleep"/>
</bean>
结合通知和切点
<!-- 切点仅仅是定义了故事发生的地点,还有故事发生的时间以及最重要的故事的内容,就是通知了,我们需要把通知跟切点结合起来 -->
<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="sleepHelper"/>
<property name="pointcut" ref="sleepPointCut"/>
</bean>
切入点和通知部署完成,接下来创建代理对象
<!-- 切入点和通知都配置完成,接下来该调用ProxyFactoryBean产生代理对象了 -->
<bean id = "humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="human"/>
<property name="interceptorNames" value="sleepHelperAdvisor"/>
<property name="proxyInterfaces" value="ssm.aop.Sleep"/>
</bean>
测试类
public class TestAoP {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Sleep sleep = (Sleep)applicationContext.getBean("humanProxy");
sleep.sleep();
}
}
运行结果:
睡觉前.......脱衣服
睡觉.......
起床后......穿衣服
Process finished with exit code 0
也可以简化配置,Spring提供了自动代理的功能,能让切点和通知进行自动匹配,简化之后的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="human" class="ssm.aop.Human"/>
<bean id="sleepHelper" class="ssm.aop.SleepHelper"/>
<bean id="sleepAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="sleepHelper"/>
<property name="pattern" value=".*sleep"/>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
方式二:Aspect注解
需要pom.xml加入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
在睡觉辅助类上加入@Aspect注解
@Aspect // 标志切面
public class SleepHelper1 {
public SleepHelper1() {
}
@Pointcut("execution(* *.sleep())") // 指定了切点
public void sleepPoint() {}
@Before("sleepPoint()") // 指定了运行时通知
public void beforeSleep() {
System.out.println("睡觉前.......脱衣服");
}
@AfterReturning("sleepPoint()")
public void afterSleep() {
System.out.println("起床后......穿衣服");
}
}
配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id ="human" class="ssm.aop.Human"/>
<bean id="sleepHelper" class="ssm.aop.SleepHelper1"/>
<!-- 自动扫描被aspectj注解的类 -->
<aop:aspectj-autoproxy />
</beans>
测试类:
public class TestAoP {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop/applicationContext-aspect.xml");
Sleep human = (Sleep)applicationContext.getBean("human");
human.sleep();
}
}
运行结果:
睡觉前.......脱衣服
睡觉.......
起床后......穿衣服
Process finished with exit code 0
方式三、纯POJO实现
之前代码不变,修改配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="human" class="ssm.aop.Human"/>
<bean id="sleepHelper" class="ssm.aspect.SleepHelper"/>
<aop:config>
<aop:aspect ref="sleepHelper">
<aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))"/>
<aop:after method="afterSleep" pointcut="execution(* *.sleep(..))"/>
</aop:aspect>
</aop:config>
</beans>
运行结果:
睡觉前.......脱衣服
睡觉.......
起床后......穿衣服
Process finished with exit code 0
IoC实例参考传送门https://blog.csdn.net/gloomy_114/article/details/68946881
AoP实现方式传送门:https://blog.csdn.net/udbnny/article/details/5870076