spring的核心两个东西:依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)。java
1、DI功能是如何实现的spring
任何一个有实际应用的程序(确定比helloword复杂)都会有两个或者更多的类组成。传统作法就是每一个对象负责管理与本身相互协做的对象的引用,这会致使express
高度耦合和难以测试。编程
public class RescueKnight implements Knight{ private RescueQuest quest; public RescueKnight(){ this.quest = new RescueQuest();--->高度耦合 } public void excuteQuest(){ quest.excute(); } }
这样一个类,就有很大的局限性,RescueKnight就只能执行rescue任务,不能执行其余任务,好比杀一只巨龙。测试
public class BraveKnight implements Knight{ private Quest quest; public BraveKnight(Quest quest){ this.quest=quest; } public void excuteQuest(){ quest.excute(); } }
这个类就厉害了,它经过构造器传入一个Quest类型的任务,这个骑士除了很brave之外,还能执行救援,杀龙等任务。这就是DI实现的最大收益,松耦合。若是一个对象只经过接口来代表依赖,而不是具体的实现,那么就能在绝不知情的状况下用具体实现进行替换。this
bean装配spa
spring有多种bean装配方式,xml文件是比较常见的例子日志
<bean id="knight" class="com.qunhe.hengli.springtest.BraveKnight"> <constructor-arg ref="quest"/> </bean> <bean id="quest" class="com.qunhe.hengli.springtest.KillDragonQuest"> <constructor-arg /> </bean>
等同于java代码code
@Configuration public void KnightConfig{ @Bean public Knight knight(){ return new BraveKnight(quest()); } @Bean public Quest quest(){ return new KillDragonQuest(); } }
再看上面代码,尽管BraveKnight依赖于Quest,可是并不知道这个Quest传过来的是什么类型的参数,也不知道来自哪里。你只有经过xml配置才知道这些组件这么装配xml
起来,这样也就能在不改变依赖的状况下修改依赖。实现代码的时候你只要加载xml配置文件,就能得到对象的引用。
方法的调用
public class KnightMain { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(".xml"); Knight knight = context.getBean(Knight.class); knight.excuteQuest(); } }
这个类彻底不知道这个骑士执行了什么任务,且不知道是哪一个骑士来执行的。
2、面向切面编程
面向切面编程是为了促使软件系统实现关注点的一项技术。好比一个系统由多个组件组成,各个组件负责一块特定的功能,可是每每它须要负责功能以外的一些事情。好比打日志功能,这些常常要融入到有核心业务的组件里面去,若是在有核心业务的组件里面去实现打日志功能那么代码会变的很混乱,切若是打日志代码的逻辑须要修改,那么须要到各个组件中修改,即便把打日志写成一个模块,各个组件里面仍是会调用这个模块的方法。这样组件不只仅关注本身的核心功能,还须要知道各个对象须要记日志,还要亲自执行这些功能。
总之使用AOP能使这些组件具备更高的内聚性,更关注本身的核心业务,不须要涉及系统服务所带来的复杂性。
好比咱们须要一个诗人来对骑士进行歌颂,这是一个记载骑士事迹的服务类。
public class Poet { public Poet(){ } public void beforeQuest(){ System.out.println("骑士你太勇敢了!"); } public void afterQuest(){ System.out.println("骑士你在这次任务中表现的真出色!"); } }
程序中poet类只有两个方法,一个是在骑士执行任务以前调用的,一个是在执行以后。下面让一个knight可使用poet:
public class BraveKnight implements Knight{ private Quest quest; private Poet poet; public BraveKnight(Quest quest,Poet poet){ this.quest=quest; this.poet=poet; } public void excuteQuest(){ poet.beforeQuest(); quest.excute(); poet.afterQuest(); } }
而后咱们只要把poet这个bean注入到knight中就好了,可是为何诗人的事须要骑士去提醒他作?应该要他自主来完成啊。而且knight的代码变复杂了,并且若是一个knight不须要poet来歌颂事迹呢?
<bean id="knight" class="com.qunhe.hengli.springtest.BraveKnight"> <constructor-arg ref="quest"/> </bean> <bean id="quest" class="com.qunhe.hengli.springtest.KillDragonQuest"> <constructor-arg /> </bean> <bean id="poet" class="com.qunhe.hengli.springtest.Poet"> <constructor-arg /> </bean> <aop:config> <aop:aspect ref="poet"> <aop:pointcut id="excuteQuest" expression="execution(* com.qunhe.hengli.springtest.Knight.excuteQuest( . . ))
and within(com.qunhe.hengli.springtest.*)) "/>--定义切点 <aop:before pointcut-ref="excuteQuest"--声明前置通知 method="beforeQuest"/> <aop:after pointcut-ref="excuteQuest"--声明后置通知 method="afterQuest"/> </aop:aspect> </aop:config>
首先声明了一个bean在<aop:aspect ref="poet">引用这个bean,<aop:before/>声明了一个前置通知,<aop:after/>声明了一个后置通知,在<aop:pointcut/>
的expression里选择要应用通知的位置,语法用的是aspectj的切点表达式语言。
execution指示器
1.第一个*表示返回值为任意值,咱们不用关心方法的返回值
2.com.qunhe.hengli.springtest表示切点仅匹配springtest这个包,能够写*表示匹配任意的包,就不用关心在哪一个包下了
3. .Knight表示匹配Knight这个接口,就不用关心类名了
4. .excuteQuest( . . )指定了方法,而且(. .),表示咱们不用关心方法传入的参数类型
and表示切点须要匹配全部指示器,固然也能够用or,not
within指示器
within(com.qunhe.hengli.springtest.*)表示在springtest包下的任意方法被调用时