1、首先明白什么叫依赖注入、控制反转,及其作用
控制反转(Inversion of Control ,Ioc)
所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护由外部容器来负责。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
依赖注入(Dependency Injection)
所谓依赖注入就是指:在运行期间,有外部容器动态地将依赖对象注入到组件中(构造方法和set方法)
好处:
1.降低组件之间的耦合度,实现软件各层之间的解耦.
2.可以使容器提供众多服务如事务管理消息服务处理等等。当我们使用容器管理事务时,开发人员就不需要手工 控制事务,也不需要处理复杂的事务传播
3.容器提供单例模式支持,开发人员不需要自己编写实现代码.
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供众多的辅佐类,使这些类可以加快应用的开发.如jdbcTemplate HibernateTemplate
个人理解依赖注入完全实现了面向接口编程,只需定义方法,无需实现,首先面向接口编程的好处就是统一编码风格。由容器创建对象,可以说是低内聚,高耦合。增加了代码的可维护性。
2、spring对于编程人员最重要的是写xml配置文件和使用注解那么我这里给几个简单的案例
依赖注入配置xml
-
<bean id="person" class="com.itheima12.spring.di.xml.setter.Person">
-
<!--
-
property就是一个bean的属性
-
name就是用来描述属性的名称
-
value就是值,如果是一般类型(基本类型和String)
-
-->
-
<property name="pid" value="1"> </property>
-
<property name="name" value="狗蛋"> </property>
-
<!--
-
spring容器内部创建的student对象给Person的student对象赋值了
-
-->
-
<property name="student">
-
<ref bean="student"/>
-
</property>
-
-
<property name="lists">
-
<list>
-
<value>list1 </value>
-
<value>list2 </value>
-
<ref bean="student"/>
-
</list>
-
</property>
-
<property name="sets">
-
<set>
-
<value>set1 </value>
-
<value>set2 </value>
-
<ref bean="student"/>
-
</set>
-
</property>
-
<property name="map">
-
<map>
-
<entry key="m1">
-
<value>map1 </value>
-
</entry>
-
<entry key="m2">
-
<ref bean="student"/>
-
</entry>
-
</map>
-
</property>
-
<property name="properties">
-
<props>
-
<prop key="p1">prop1 </prop>
-
<prop key="p2">prop2 </prop>
-
</props>
-
</property>
-
<property name="objects">
-
<list>
-
<value>obj1 </value>
-
<ref bean="student"/>
-
</list>
-
</property>
-
</bean>
-
"1.0" encoding="UTF-8" xml version=
-
<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-2.5.xsd">
-
<bean id="person" class="com.itheima12.spring.di.xml.constructor.Person">
-
<!--
-
constructor-arg指的是构造器中的参数
-
index 角标 从0开始
-
value 如果一般类型,用value赋值
-
ref 引用类型赋值
-
-->
-
<constructor-arg index="0" value="asdfsafd"> </constructor-arg>
-
<constructor-arg index="1" ref="student"> </constructor-arg>
-
</bean>
-
<bean id="student" class="com.itheima12.spring.di.xml.constructor.Student"> </bean>
-
</beans>
-
"1.0" encoding="UTF-8" xml version=
-
<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"
-
xsi:schemaLocation= "http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
-
http://www.springframework.org/schema/context
-
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
-
<!--
-
component:把一个类放入到spring容器中,该类就是一个component
-
在base-package指定的包及子包下扫描所有的类
-
-->
-
<context:component-scan base-package="com.itheima12.spring.scan">
-
</context:component-scan>
-
</beans>
好了这里就主要介绍了这些,还有很多遇到时在看文档吧,百度吧(包括单例,多例,事务 ,aop,继承,初始化销毁(
-
<bean id="helloWorld" class="com.itheima12.spring.initdestroy.HelloWorld"
-
init-method= "init"
-
destroy-method= "destroy"> </bean>
),懒加载,别名,工厂方法创建对象等等,都可以看案例。
3、我们来学习aop
切面(Aspect):其实就是共有功能的实现。如日志切面、权限切面、事务切面等。在实际应用中通常是一个存放共有功能实现的普通Java类,之所以能被AOP容器识别成切面,是在配置中指定的。
通知(Advice):是切面的具体实现。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。
连接点(Joinpoint):就是程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等,但spring只支持方法级的连接点。
切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。
目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能代码等待AOP容器的切入。
代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。
织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现
意义:在开发的时候,各个切面和目标类是完全松耦合的,但是最终生成的代理对象的方法把这几个代理对象的内容就结合起来了。
通知介绍
拦截环绕通知
-
/**
-
* 环绕通知
-
* joinPoint.proceed();这个代码如果在环绕通知中不写,则目标方法不再执行
-
* 能控制目标方法的执行
-
*前置通知和后置通知能在目标方法的前面和后面加一些代码,但是不能控制目标方法的执行
-
*/
-
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
-
System.out.println( "begin");
-
joinPoint.proceed(); //调用目标方法
-
System.out.println( "end");
-
}
在Spring中最基础的通知类型是拦截环绕通知(interception around advice)。
Spring里使用方法拦截的环绕通知兼容AOP联盟接口。
前置通知
个更简单的通知类型是前置通知(before advice)。 它不需要MethodInvocation对象,因为它只是在进入方法之前被调用。
前置通知的一个主要优点是它不需要调用proceed()方法,因此就不会发生无意间运行拦截器链失败的情况。
-
/*
-
-
前置通知
-
1、在目标方法执行之前
-
2、获取不到目标方法的返回
-
<aop:before method="beginTransaction" pointcut-ref="perform"/>
-
-
* 参数:连接点
-
*/
-
public void beginTransaction(JoinPoint joinPoint){
-
String methodName = joinPoint.getSignature().getName();
-
System.out.println( "连接点的名称:"+methodName);
-
System.out.println( "目标类:"+joinPoint.getTarget().getClass());
-
System.out.println( "begin transaction");
-
}
异常通知
-
<!--
-
异常通知
-
-->
-
<aop:after-throwing method= "throwingMethod" throwing= "ex" pointcut-ref= "perform"/>
-
/**
-
* 异常通知
-
* 接受目标方法抛出的异常
-
*/
-
public void throwingMethod(JoinPoint joinPoint,Throwable ex){
-
System.out.println(ex.getMessage());
-
}
后置通知
-
/**
-
* 后置通知
-
* 在目标方法执行之后
-
1、后置通知可以获取到目标方法的返回值
-
2、当目标方法抛出异常,后置通知将不再执行
-
<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>
-
-
*/
-
public void commit(JoinPoint joinPoint,Object val){
-
System.out.println( "目标方法的返回值:"+val);
-
System.out.println( "commit");
-
}
后置通知可以访问返回值(但不能进行修改),被调用方法,方法参数以及目标对象。
最终通知
-
/**
-
* 最终通知
-
无论目标方法是否抛出异常都将执行
-
<aop:after method="finallyMethod" pointcut-ref="perform"/>
-
-
*/
-
public void finallyMethod(){
-
System.out.println( "finally method");
-
}
引入通知
Spring 把引入通知(introduction advice)作为一种特殊的拦截通知进行处理。
引入通知需要一个IntroductionAdvisor,和一个IntroductionInterceptor,后者实现下面的接口:
Public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
继承自AOP联盟MethodInterceptor 接口的invoke()方法,必须确保实现引入:也就是说,如果被调用的方法位于一个已经被引入接口里,这个引入拦截器将负责完成对这个方法的调用--因为它不能调用proceed()方法。
引入通知不能和任何切入点一起使用,因为它是应用在类级别而不是方法级别。
xml文件配置
-
<aop:config>
-
<!-- 面 -->
-
<aop:aspect ref="cacheInterceptor">
-
-
-
<!-- 点 -->
-
<!-- 环绕通知 -->
-
<aop:around method="doAround" pointcut="execution(* cn.itcast.core.service.*.*.get*(..))"/>
-
<!-- 变更 -->
-
<!-- 后知通知 -->
-
<aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.update*(..))"/>
-
<aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.add*(..))"/>
-
<aop:after method="doAfter" pointcut="execution(* cn.itcast.core.service.*.*.delete*(..))"/>
-
-
<!--前置通知-->
-
<!-- <aop:before method="" pointcut-ref=""/> -->
-
-
</aop:aspect>
-
</aop:config>
切入点表达式一般格式
![](https://i-blog.csdnimg.cn/blog_migrate/150f32db9ffff3dfbac5e76cb2219ca4.png)
Spring AOP 用户可能会经常使用 execution切入点指示符。执行表达式的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外, 所有的部分都是可选的。
在service包或其子包中定义的任意方法的执行:(国家电力)
execution(* com.xyz.service..*.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))