什么是Spring
Spring是J2EE应用程序轻量级框架,它一种非侵入的方式来管理你的代码。
IOC(Inversion of Control)控制反转
对象创建责任的反转,在Spring中的BeanFactory是IOC容器的核心接口,
负责实力恶化,定位,配置应用程序中的对象及建立这些对象间的依赖。
XmlBeanFactory实现BeanFactory接口,通过获取xml配置文件数据,
组成应用对象及对象间的依赖关系。
Spring中三种注入方式:set注入(必须要有无参构造方法),接口注入,构造方法注入
- xml 方式
<bean id="helloWorld" // 标识符
class="com.test.HelloWorld" // 类全名
lazy-init="true" // 延迟初始化,第一次获取时才初始化,默认是false(spring容器启动时就初始化)
scope="prototype" // 添加此属性后bean可以创建多实例(spring默认创建的是单列),
但是spring不会再管理这些实例的生命周期,谁用 谁管理。
而且添加此属性后lazy_init="true"失效。
init-method="init" // 实例化bean之后调用的方法
destroy-method="destroy" // 销毁bean之前调用的方法
<!--
property描述的就是bean中的属性
name属性就是描述属性的名称
value就是值 如果是基本属性(String),就用value赋值
ref 如果是引用类型,用ref赋值
-->
<property name="pid" value="1"></property>
<property name="name" value="王二麻子"></property>
<property name="student" ref="student"></property>
// 对象作为属性,也可以这样:
<!-- <property name="student">
<ref bean="student"/>
</property> -->
<!--
constructor-arg 代表某一个构造器的参数
index 构造器参数的下标
value
ref
type 类型
-->
<constructor-arg index="0" value="1" type="java.lang.String"></constructor-arg>
<constructor-arg index="1" ref="student"></constructor-arg>
></bean>
<alias name="helloWorld" alias="骑士"/>
<alias name="helloWorld" alias="皇马"/>
<alias name="helloWorld" alias="中国女排"/> // 别名
<!-- 工厂方法创建实例 -->
<bean id="helloWorld2"
class="com.test.HelloWorldFactory"
factory-method="getInstance"></bean>
<bean id="helloWorldFactory" class="com.test.HelloWorldFactory2"></bean>
<!--
factory-bean指向了实力工厂的bean
factory-method实例工厂对象的方法
-->
<bean id="helloWorld3" factory-bean="helloWorldFactory" factory-method="getInstance"></bean>
<!-- 工厂方法创建 -->
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = (HelloWorld)context.getBean("helloWorld");
HelloWorld helloWorld2 = (HelloWorld)context.getBean("骑士");
HelloWorld helloWorld3 = (HelloWorld)context.getBean("helloWorld2");
HelloWorld helloWorld4 = (HelloWorld)context.getBean("helloWorld3");
如果scope不是"prototype"的话获取的都是一个对象
- 注解
- 配置文件中加入<context:annotation-config></context:annotation-config>
@Autowired/@Resource:这两个注解的区别是:@Autowired 默认按类型装配,
@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
都可以作用在字段上和setter方法上。
@Autowired:注解是按类型装配依赖对象默认情况下它要求依赖对象必须存在,
如果允许null值,可以设置它required属性为false。
如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
@Resource注解默认按名称装配。
名称可以通过@Resource的name属性指定,如果没有指定name属性,
当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象
当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
@PostConstruct :指定Bean的初始化方法
@PreDestroy :指定Bean的销毁方法 - 扫描方式,在配置文件中加入 <context:component-scan base-package="cn.test"/>
其中base-package为需要扫描的包(含子包)。
1、在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,连个组件都会被自动检测并织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。
2、功能介绍
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
- 配置文件中加入<context:annotation-config></context:annotation-config>
AOP面向切面编程
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
-
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点, 所以在Spring中连接点指的就是被拦截到的方法, 实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码, 通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
<aop:config>
<aop:pointcut
// 切入点表达式
expression="execution(* com.test.PersonDaoImpl.*(..))"
id="perform"/>
<aop:aspect ref="transaction">
<!--
前置通知
1、在目标方法执行之前
2、前置通知中,方法有一个参数JoinPoint
-->
<!--
<aop:before method="beginTransaction" pointcut-ref="perform"/>
-->
<!--
后置通知
1、在目标方法执行之后
2、能够获取目标方法的返回值
returning="val"
3、如果目标方法产生异常,则后置通知不再执行
-->
<!--
<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>
-->
<!--
异常通知
获取目标方法抛出的异常信息
throwing="ex"
-->
<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
<!--
最终通知
-->
<aop:after method="finallyMethod" pointcut-ref="perform"/>
<!--
环绕通知
能够控制目标方法的执行
环绕通知可以有返回值,这个返回值就是代理对象的方法的返回值
前置通知和后置通知只能在目标方法执行之前和之后加代码,但是不能控制目标方法的执行
-->
<aop:around method="aroundMethod" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
常用切入点表示式
任意公共方法的执行:
execution(public * *(..))
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
在service包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service.*)
在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service..*)
实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):
this(com.xyz.service.AccountService)
'this'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得代理对象在通知体内可用。
实现AccountService接口的目标对象的任意连接点 (在Spring AOP中只是方法执行):
target(com.xyz.service.AccountService)
'target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得目标对象在通知体内可用。
任何一个只接受一个参数,并且运行时所传入的参数是Serializable 接口的连接点(在Spring AOP中只是方法执行)
args(java.io.Serializable)
'args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得方法参数在通知体内可用。
请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args版本只有在动态运行时候传入参数是Serializable时才匹配,而execution版本在方法签名中声明只有一个 Serializable类型的参数时候匹配。
目标对象中有一个 @Transactional 注解的任意连接点 (在Spring AOP中只是方法执行)
@target(org.springframework.transaction.annotation.Transactional)
'@target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
任何一个目标对象声明的类型有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行):
@within(org.springframework.transaction.annotation.Transactional)
'@within'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
任何一个执行的方法有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
任何一个只接受一个参数,并且运行时所传入的参数类型具有@Classified 注解的连接点(在Spring AOP中只是方法执行)
@args(com.xyz.security.Classified)
'@args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。
任何一个在名为'tradeService'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):
bean(tradeService)
任何一个在名字匹配通配符表达式'*Service'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):
bean(*Service)
Spring JDBC事务管理
<!--
事务管理器
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--
声明事务策略
id是标示符
transaction-manager 事务管理器
-->
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<!--
告诉spring容器什么样的目标方法采用什么样的事务策略
name 用来说明目标方法
save* 以save开头的目标方法
isolation 隔离属性
propagation 传播属性
是解决事务嵌套问题的
read-only
为true:只读事务 只能读
为false:读写事务
-->
<tx:method name="save*"
isolation="DEFAULT"
propagation="REQUIRED"
read-only="false"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut
expression="execution(* com.itheima11.spring.jdbc.transaction.service.impl.*.*(..))"
id="perform"/>
<aop:advisor advice-ref="tx" pointcut-ref="perform"/>
</aop:config>