1.IOC与DI
-
inverse of control 控制反转
我们创建对象的方式反转了。以前对象的创建由开发人员自己维护,包括依赖注入也是自己注入。使用spring之后,对象的创建及依赖关系可以由spring完成创建及依赖注入。
控制反转反转的是对象的创建方式,由自己创建改为由程序创建
-
dependency injection依赖注入
实现IOC思想需要di支持。
注入方法:set方法注入,构造方法注入,字段注入
注入类型:值类型注入(8大基本数据类型),引用类型注入
2.BeanFactory与ApplicationContext
-
BeanFactory
spring原始接口,针对原始接口的实现类的功能比较单一
BeanFactory接口实现类的容器,特点是每次在获得对象时才会创建对象
-
ApplicationContext
每次容器启动时就会创建容器中配置的所有对象
ClassPathXmlApplicationContext:从类路径下加载配置文件 FileSystemXmlApplicationContext:从磁盘绝对路径下加载配置文件
-
如果想让Spring容器随项目的创建而创建,随项目的关闭而销毁,需要在web.xml中监听Application创建销毁监听器(
ServletContextListener
)的实现类org.springframework.web.context.ContextLoaderListener
并通过<context-param>来指定配置文件位置<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
web开发中使用ApplicationContext,在资源匮乏的环境下可以使用BeanFactory
3.配置详解
-
Bean元素 ---- 描述需要Spring容器管理的对象
-
name
给被管理对象起个名称,获得对象时,根据该名成获得对象,可以重复,可以使用特殊字符
-
class
被管理对象的完整类名
-
scope
- singleton:单例对象(默认值),标识为单例的对象在spring容器中只会被创建一次
- prototype:多例原型,被标识为多例的对象,每次获得时才会创建,每次创建时都是新的对象,整合struts2时,ActionBean必须为多例-->
-
init-method
配置一个方法为生命周期的初始方法,spring会在创建对象后立即调用
-
destroy-method
配置一个方法为生命周期的销毁方法,spring容器在关闭并销毁所有容器中的对象之前调用
<bean name="user" class="com.spring.learn1.entity.User" scope="singleton" init-method="init" destroy-method="destory"/>
-
-
导入其他Spring配置文件
<import resource="com/spring/learn1/applicationContext.xml"/>
-
Spring 创建对象的方式
-
空参创建
<bean name="user" class="com.spring.learn1.entity.User"/>
-
静态工厂
//调用UserFactory的createUser方法来创建对象,createUser为静态方法 <bean name="user" class="com.spring.learn1.entity.UserFactory" factory-method="createUser"/>
-
实例工厂
//创建UserFactory对象,调用UserFactory的createUser方法来创建对象 <bean name="user" factory-bean="userFactory" factory-method="createUser"/> <bean factory-bean="userFactory" class="com.spring.learn1.entity.UserFactory"/>
-
-
Spring属性注入方式
-
set方法注入
//name:属性名称; value:值; ref:对象 <bean name="user" class="com.spring.learn1.entity.User"> <property name="name" value="tom"/> <property name="age" value="18"/> <property name="car" ref="car"/> </bean> <bean name="car" class="com.spring.learn1.entity.Car"> <property name="carName" value="玛莎拉蒂"/> </bean>
-
构造函数注入
//constructor-arg:构造参数 name:参数名称 type:参数类型 index:注入位置 ref:对象 value:值 <bean name="user" class="com.spring.learn1.entity.User"> <constructor-arg name="name" type="java.lang.String" index="0" value="tom"/> <constructor-arg name="age" type="java.lang.Integer" index="1" value="20"/> <constructor-arg name="car" ref="car" index="2"/> </bean>
-
p名称空间注入
xmlns:p="http://www.springframework.org/schema/p" <bean name="user" class="com.spring.learn1.entity.User" p:name="jack" p:age="16" p:car-ref="car"/> <bean name="car" class="com.spring.learn1.entity.Car"> <property name="carName" value="玛莎拉蒂"/> </bean>
-
spel(Spring Expression Language,Spring 表达式语言)注入
<bean name="user" class="com.spring.learn1.entity.User"> <constructor-arg name="name" type="java.lang.String" index="0" value="tom"/> <constructor-arg name="age" type="java.lang.Integer" index="1" value="20"/> <constructor-arg name="gender" type="java.lang.String" index="2" value="男"/> <constructor-arg name="car" ref="car" index="3"/> </bean> <bean name="car" class="com.spring.learn1.entity.Car"> <property name="carName" value="玛莎拉蒂"/> </bean> <bean name="user2" class="com.spring.learn1.entity.User"> <property name="name" value="#{user.name}"/><!-- 引用user的name --> <property name="gender" value="#{'女'}"/><!-- 定义为女 --> <property name="age" value="#{user.getAge()}"/><!-- 通过调用方法设置age --> <property name="car" ref="car"/><!-- 引用对象采用此方式 --> </bean>
-
-
Spring复杂类型注入
数组
list
map
-
properties
<bean name="ot" class="com.spring.learn1.entity.OT"> <!-- 如果数组中只准备注入一个值或者一个对象,直接使用value或ref即可 --> <!--<property name="sUsers" ref="user"/>--> <property name="sUsers"> <array> <value>"tom"</value> <value>"jerry"</value> <ref bean="user2"/> </array> </property> <!-- 如果list,set中只准备注入一个值或者一个对象,直接使用value或ref即可 --> <!--<property name="userList" ref="user"/>--> <property name="userList"> <list> <value>"tom"</value> <value>"jerry"</value> <ref bean="user2"/> </list> </property> <!-- Map注入 --> <property name="userMap"> <map> <entry key="name" value="tom"/> <entry key="age" value="14"/> <entry key="lover" value-ref="user2"/> <entry key-ref="user2" value-ref="user"/> </map> </property> <property name="userProperties"> <props> <prop key="user" >root</prop> <prop key="password">123</prop> </props> </property> </bean>
4.Spring生命周期管理
-
web.xml配置
添加监听器
org.springframework.web.context.ContextLoaderListener
-
指定配置文件位置
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
-
获取
WebApplicationContextUtils.getWebApplicationContext(ServletContext)
5.AOP
-
底层实现原理(采用以下两种混合式开发,有接口优先采用动态代理)
-
动态代理(实现InvocationHandler接口)
被代理对象必须要实现接口才能产生代理对象
public class UserFactory implements InvocationHandler { UserService userService; public UserFactory(UserService userService) { this.userService = userService; } public UserService getUserServiceProxy(){ return (UserService) Proxy.newProxyInstance(UserFactory.class.getClassLoader(),UserServiceImpl.class.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开启事务"); method.invoke(userService,args); System.out.println("关闭事务"); return null; } }
-
cglib代理(第三方代理技术,实现MethodInterceptor接口)
可以对任何类生成代理,原理是对目标对象进行继承代理,如果目标对象被final修饰,则该类无法对该对象代理
public class MyCglbProxy implements MethodInterceptor { public UserServiceImpl getProxy() { Enhancer enhancer = new Enhancer();//帮我们生成代理对象 enhancer.setSuperclass(UserServiceImpl.class);//设置代理的目标 enhancer.setCallback(this);//代理的操作 return (UserServiceImpl) enhancer.create();//创建代理对象 } @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("开启事务"); methodProxy.invokeSuper(object, args); System.out.println("关闭事务"); return null; } }
-
-
AOP相关术语
- Joinpoint(连接点):目标对象中可以进行增强的方法,即可以被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点.
- Pointcut(切入点):目标对象中已经增强的方法,所谓切入点是指我们要对哪些 Joinpoint 进行拦截增强.
- Advice(通知/增强):需要增强的代码,所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类
动态地添加一些方法或Field. - Target(目标对象):代理的目标对象
- Weaving(织入):是指把Advice应用到Target来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
- Proxy(代理) :一个类被 AOP 织入增强后,就产生一个结果代理类
- Aspect(切面): 是切入点和通知(引介)的结合
-
AOP开发步骤
定义目标对象
-
定义增强类
public class MyAdvice { // 前置通知 public void before() { System.out.println("这是前置通知"); } // 后置通知(如果出现异常不会通知) public void after() { System.out.println("这是后置通知,如果出现异常不会调用"); } // 异常通知 public void afterException() { System.out.println("这是异常通知"); } // 最终通知(无论是否出现异常,都会进行通知) public void end() { System.out.println("这是最终通知,无论是否出现异常,都会进行通知"); } // 环绕通知(通知之前之后都会调用,切面要完成的功能) public Object around(ProceedingJoinPoint point) throws Throwable { System.out.println("这是环绕通知,之前的部分"); Object proceed = point.proceed();//调用目标方法 System.out.println("这是环绕通知,之后的部分"); return proceed; } }
-
将通知织入目标对象
导入aop约束命名空间
aop
-
配置目标对象
<!--配置目标对象--> <bean class="com.spring.learn3.entity.UserServiceImpl" name="target"/>
-
配置通知对象
<!--配置通知对象--> <bean class="com.spring.learn3.entity.MyAdvice" name="advice"/>
-
将通知织入目标对象
<!--织入--> <aop:config> <!--配置切入点--> <aop:pointcut id="user" expression="execution(* com.spring.learn3.entity.*ServiceImpl.*(..))" <aop:aspect ref="advice"> <!--前置通知(1)--> <aop:before method="before" pointcut-ref="user"/> <!--最终通知(2)--> <aop:after method="end" pointcut-ref="user"/> <!--环绕通知(3)--> <aop:around method="around" pointcut-ref="user"/> <!--后置通知(4)--> <aop:after-returning method="after" pointcut-ref="user"/> <!--异常通知(5)--> <aop:after-throwing method="afterException" pointcut-ref="user"/> </aop:aspect> </aop:config> //正常调用顺序1-3-4-3-2 //异常调用顺序1-3-5-2
6)Spring整合JDBC----JdbcTemplate
-
JdbcTemplate使用方法
//准备连接池 ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://39.106.1.213:3306/hb?characterEncoding=utf-8"); dataSource.setUser("root"); dataSource.setPassword("123456"); //建立JDBC模板对象 JdbcTemplate template = new JdbcTemplate(dataSource); //执行sql语句 String sql = "insert into User values(null,'肉丝')"; template.update(sql);
-
Spring整合JDBC
-
Dao实现类中添加JdbcTemplate属性,实现set方法,依赖关系Dao---->JdbcTemplate---->DataSource
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://39.106.1.213:3306/hb? characterEncoding=utf-8"/> <property name="user" value="xxx"/> <property name="password" value="xxx"/> </bean> <bean name="template" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean name="userDao" class="com.dao.test1.entity.UserDaoImpl"> <property name="template" ref="template"/> </bean>
Dao继承JdbcDaoSupport类,该类设置连接池时自动创建JdbcTemplate,依赖关系Dao---->DataSource
-
-
读取外部Properties
-
建立properties文件
//src目录下db.properties jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://39.106.1.213:3306/hb?characterEncoding=utf-8 jdbc.user=root jdbc.password=123456
-
配置
<!--指定Spring读取db.properties--> <context:property-placeholder location="classpath:db.properties"/> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean>
-
7)Spring事务
-
平台事务管理器
PlatformTransactionManager() ---- DataSourceTransactionManager(使用JDBC或iBatis进行持久化数据时使用) ---- HibernateTransactionManager(使用Hibernate进行持久化数据时使用)
-
事务定义信息TransactionDefinition
事务的隔离级别
是否只读
-
事务的传播行为(决定业务方法之间调用,事务应该如何处理)
保证同一个事务中
PROPAGATION_REQUIRED 支持当前事务,如果不存在,就新建一个(默认)
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常-
保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
超时信息
事务的状态 TransactionStatus
-
Spring管理事务方式
-
编码式 ---- 使用模板(TransactionTemplate)执行事务
-
调用模板执行事务
private TransactionTemplate transactionTemplate; transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { //数据库业务 } });
-
Spring中进行配置
-
将核心事务管理器配置到spring
<context:property-placeholder location="classpath:db.properties"/> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事务核心管理器,封装了所有事务操作,依赖连接池--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
配置TransactionTemplate模板
<!--事务模板对象--> <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>
-
配置操作事务的类,将事务模板注入
<bean name="" class=""> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>
-
-
-
xml配置(AOP)
-
导入约束
beans: 最基本
context:读取properties配置
aop:配置aop
tx:配置事务通知 -
配置通知(以方法为参数进行配置)
<context:property-placeholder location="classpath:db.properties"/> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事务核心管理器,封装了所有事务操作,依赖连接池--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置事务通知 --> <tx:advice transaction-manager="transactionManager"> <!-- name:方法名称 isolation="DEFAULT" 隔离级别 propagation="REQUIRED" 传播行为 read-only="false" 只读 timeout="-1" 过期时间 rollback-for="" -Exception no-rollback-for="" +Exception --> <tx:attributes> <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/> <tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/> </tx:attributes> </tx:advice>
-
织入
<!-- 配置织入--> <aop:config> <!-- 配置切点表达式--> <aop:pointcut id="txPc" expression="execution(* com.dao.test2.AccountServiceImpl.transfer())"/> <!-- 配置切面:通知+切点--> <!-- advice-ref:通知名称--> <!-- pointcut-ref:切点名称--> <aop:advisor advice-ref="txadvice" pointcut-ref="txPc"/> </aop:config>
-
-
注解配置(AOP)
导入约束
-
配置xml
<context:property-placeholder location="classpath:db.properties"/> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事务核心管理器,封装了所有事务操作,依赖连接池--> <bean name="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transaction"/>
-
类或方法添加注解
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
-