Spring 几大核心功能
Ioc/DI 控制反转/依赖注入
AOP 面向切面编程
声明式事务
Spring框架组成
test: spring 提供测试功能 Core Container: 核心容器.Spring 启动最基本的条件. Beans : Spring 负责创建类对象并管理对象 Core: 核心类 Context: 上下文参数.获取外部资源或这管理注解等 SpEL: expression.jar
AOP: 实现 aop 功能需要依赖 Aspects: 切面 AOP 依赖的包
Data Access/Integration : spring 封装数据访问层相关内容 JDBC : Spring 对 JDBC 封装后的代码. ORM: 封装了持久层框架的代码.例如 Hibernate transactions:对应 spring-tx.jar,声明式事务使用.
WEB:需要 spring 完成 web 相关功能时需要. 例如:由 tomcat 加载 spring 配置文件时需要有 spring-web包
Spring框架中的重要概念
容器(Container):Spring当作一个大的容器,存放对象.
老版本中使用BeanFactory 新版本中使用ApplicationContext对象
Ioc((Inversion of Control)
概述:IoC 完成的事情原先由程序员主动通过 new 实例化对象事情,转交给 Spring 负责.
控制反转中的控制指的是:控制类的对象
控制反转中的反转指的是:转交给Spring管理
IoC最大的作用:解耦---程序员不需要管理对象,解除了对象管理与程序员之间的耦合
Spring配置文件
Spring配置文件约束基于schema schema文件扩展名为.xsd 每一次引入一个xsd文件为创建了一个命名空间(xmlns)
通过bean标签创建对象, 当配置文件被加载的时,创建对象
例:
<?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.xsd"> <bean id="peo" class="com.bjsxt.pojo.People"> </bean> </beans>
获取bean的方法:getBean("bean标签的id值",返回值类型) 若不填写返回值则返回值类型默认为Object
例:
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //Object peo = ac.getBean("peo"); People people = ac.getBean("peo",People.class);
获取Spring配置文件中所有创建的对象方法
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); String[] names = ac.getBeanDefinitionNames();
Spring创建对象的三种方式:
通过构造方法创建 通过无参构造方法创建:默认通过此方法创建 通过有参构造方法创建:需要配置 需要在实体类中提供有参构造方法 需要在配置文件中配置执行哪个构造方法,若存在多个构造方法,则执行最后一个 例:
<bean id="peo" class="com.bjsxt.pojo.People"> <constructor-arg index="0" name="id" type="int" value="123"></constructor-arg> <constructor-arg index="0" name="name" type="java.lang.String" value="张三"></constructor-arg> </bean>
通过实例工厂创建
public class PeopleFactory { public People newInstance(){ return new People(1,"测试"); } }
<bean id="factory" class="com.bjsxt.pojo.PeopleFactory"></bean> <bean id="peo1" factory-bean="factory" factory-method="newInstance"></bean>
通过静态工厂创建
public class PeopleFactory { public Static People newInstance(){ return new People(1,"测试"); } }
<bean id="peo2" class="com.bjsxt.pojo.PeopleFactory" factory-method="newInstance"></bean>
给Bean对象的属性赋值
设置注入-适用于基本数据类型
<bean id="peo" class="com.bjsxt.pojo.People"> <property name="id" value="222"></property> <property name="name" value="张三"></property> </bean>
若为Set集合
<property name="sets"> <set> <value>1</value> <value>2</value> <value>3</value> <value>4</value> </set> </property>
若为List
<property name="list"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property>
若List中只有一个值
<property name="list" value="1"></property>
若是数组
<property name="strs" > <array> <value>1</value> <value>2</value> <value>3</value> </array> </property>
若是Map
<property name="map"> <map> <entry key="a" value="b"></entry> <entry key="c" value="d"></entry> </map> </property>
若是Properties
<property name="demo"> <props> <prop key="key">value</prop> <prop key="key1">value1</prop> </props> </property>
DI-依赖注入(Dependency Injection)
当一个类(A)中需要依赖另一个类()对象时,把 B 赋值给 A 的过 程就叫做依赖注入. 例:
<bean id="peo" class="com.bjsxt.pojo.People"> <property name="desk" ref="desk"></property> </bean> <bean id="desk" class="com.bjsxt.pojo.Desk"> <property name="id" value="1"></property> <property name="price" value="12"></property> </bean>
People类含有Desk类这一属性,通过ref 标签注入
Spring整合Mybatis
配置文件ApplicationContext.xml
<?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.xsd"> <!-- 数据源封装类 .数据源:获取数据库连接,spring-jdbc.jar中--> <bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/ssm"></property> <property name="username" value="root"></property> <property name="password" value="yu752949273"></property> </bean> <!-- 创建SqlSessionFactory对象 --> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据库连接信息来源于dataSource --> <property name="dataSource" ref="dataSouce"></property> </bean> <!-- 扫描器相当于mybatis.xml中 mappers下package标签,扫描com.bjsxt.mapper包后会给对应接口创建对象--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 要扫描哪个包 --> <property name="basePackage" value="com.bjsxt.mapper"></property> <!-- 和factory产生关系 --> <property name="sqlSessionFactory" ref="factory"></property> </bean> </beans>
AOP面向切面编程(Aspect Oriented Programming)
程序正常执行流程为纵向顺序执行. AOP为在原有纵向执行的流程中添加一个横切面,运行到此处的时优先横向执行切面中的代码,再向下执行. 优点: 不需要修改原有的代码 释放部分逻辑,降低耦合
原有功能:切点 pointcut 前置通知:在切点前执行 before advice 后置通知:在切点后执行 after advice 异常通知:切点执行中发生异常 会触发异常通知 throws advice 织入:把切面嵌入到原有功能的过程叫做织入
Spring提供两种AOP方式:
Schema-based: 每个通知都需要实现接口或类 配置 spring 配置文件时在aop:config配置
AspectJ: 每个通知不需要实现接口或类 配置 spring 配置文件是在aop:config的aop:aspect子标签中配置
Schema_based实现方式
需要的jar aopalliance.jar aspectjweaver.jar
新建通知类
前置通知类 实现MethodBeforeAdvice方法 其中:arg0为切点方法对象 arg1为切点方法参数 arg2为切点类对象
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("前置通知"); } }
后置通知 实现AfterReturningAdvice方法 其中:arg0为切点方法返回值对象 arg1为切点方法对象 arg2为切点方法参数 arg3切点类对象
import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class MyAfterAdvice implements AfterReturningAdvice{ @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("后置通知"); } }
环绕通知 实现MethodInterceptor方法 invoke方法有返回值,返回值为切点函数对象 arg0为切点代理类
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { System.out.println("环绕通知-前"); Object o= arg0.proceed();//放行,调用切点函数 System.out.println("环绕通知-后"); return o; } }
异常通知 实现ThrowsAdvice方法-有两种方法不参数不同 其中:异常通知的方法名称必须为afterThrowing,异常可以是具体的异常,但是一旦异常类型不对应,则不触发异常通知,一般为Exception父类
import org.springframework.aop.ThrowsAdvice; public class MyThrowAdvice implements ThrowsAdvice { public void afterThrowing(Exception ex) throws Throwable{ System.out.println("异常通知"); } public void afterThrowing(Method m, Object[] args,Object target, Exception ex) { System.out.println("异常通知"); } }
配置文件编写
先引入aop命名空间 配置通知类 *配置切面通配符,匹配任意方法名,任意类名,任意一级包名 如果希望匹配任意方法参数(..)
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注入通知类--> <bean id="before" class="com.ym.aop.MyBeforeAdvice"></bean> <bean id="after" class="com.ym.aop.MyAfterAdvice"></bean> <bean id="around" class="com.ym.aop.MyAroundAdvice"></bean> <bean id="throw" class="com.ym.aop.MyThrowAdvice"></bean> <!--配置AOP--> <aop:config> <!--设置切点类方法--> <aop:pointcut expression="execution(* com.ym.test.Demo.*(..))" id="mypoint"/> <!--前置通知--> <aop:advisor advice-ref="before" pointcut-ref="mypoint"/> <!--环绕通知--> <aop:advisor advice-ref="around" pointcut-ref="mypoint"/> <!--后置通知--> <aop:advisor advice-ref="after" pointcut-ref="mypoint"/> <!--异常通知--> <aop:advisor advice-ref="throw" pointcut-ref="mypoint"/> </aop:config> <!--将切点类交由Spring管理--> <bean id="demo" class="com.ym.test.Demo" ></bean> </beans>
AspectJ方式(暂时不总结)
代理设计模式
代理设计模式优点: 保护真是对象 让对象指责更加明确 扩展性 代理设计模式 真实对象(老总) 代理对象(秘书) 抽象对象(抽象功能)
静态代理设计模式
由代理对象代理所有真实对象的功能. 自己编写代理类 每个代理的功能需要单独编写 静态代理设计模式的缺点: 当代理功能比较多时,代理类中方法需要写很多.
动态代理
分类:JDK动态代理 cglib动态代理
Spring 中加载 properties 文件
eclipse中 在 src 下新建 xxx.properties 文件 idea中 再resources下新建 xxx.properties文件
引入此标签 进行加载配置文件 当引入多个配置文件时 用逗号分隔 其中classpath:为在类路径下加载 classpath*:为在所有路径下进行扫描加载
<context:property-placeholder location="classpath:db.properties"/>
添加了属性文件加载后 并且在中开启自动注入注意的地方 SqlSessionFactoryBean 的 id 不能叫做 sqlSessionFactory 把原来通过ref引用替换成value赋值,自动注入只能影响 ref,不会影响 value 赋值
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage"value="com.bjsxt.mapper"></property> <property name="sqlSessionFactoryBeanName"value="factory"></property> </bean>
在被Spring管理的类中通过@Value(“${key}”)取出properties中内容,前提是进行注解扫描且被spring管理
bean标签的scope属性
控制对象的有效范围(单例,多例),默认为单例的,无论重新获取多少次,都是同一个对象
scope可取值: singleton 默认值,单例 prototype 多例,每次获取重新实例化 request 每次请求重新实例化 session 每个会话对象内,对象是单例的 application 在 application 对象内是单例
单例模式
懒汉式
public class SingleTon { //由于对象需要被静态方法调用,把方法设置为 static //由于对象是 static,必须要设置访问权限修饰符为 private , 如果是 public 可以直接调用对象,不执行访问入口 private static SingleTon singleton; /** * 方法名和类名相同 * 无返回值. * * * 其他类不能实例化这个类对象 * * 对外提供访问入口 */ private SingleTon(){} /** * 实例方法,实例方法必须通过对象调用 * * 设置方法为静态方法 * * * @return */ public static SingleTon getInstance(){ //添加逻辑如果实例化过,直接返回 if(singleton==null){ /* * 多线程访问下,可能出现 if 同时成立的情况,添加锁 */ synchronized (SingleTon.class) { //双重验证 if(singleton==null){ singleton = new SingleTon(); } } } return singleton; } }
饿汉式:
public class Singleton { private Singleton(){} //加载此类时就进行实例化,而懒汉式只有在被调用的时候才实例化 private static Singleton singleton=new Singleton(); public static Singleton getobj(){ return singleton; } }
声明式事务
事务控制代码已经由 spring 写好.程序员只需要声明出哪些方法需要进行事务控制和如何进行事务控制 声名式事务针对ServiceImpl类下的方法 事务管理器基于AOP
<!--事务管理通知类--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务管理器类--> <tx:advice id="MyAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="ins*" propagation="REQUIRED"/> <tx:method name="sel*" propagation="REQUIRED"/> <tx:method name="up*" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--事务管理切点 切点范围尽量大一些--> <aop:config> <aop:pointcut id="MyPoint" expression="execution(* com.ym.*.*(..))"/> <aop:advisor advice-ref="MyAdvice" pointcut-ref="MyPoint"/> </aop:config>
声明式事务中属性的解释
name=”” 哪些方法需要有事务控制 支持*通配符
readonly=”boolean” 是否是只读事务: 如果为 true,告诉数据库此事务为只读事务.数据化优化,会对性能有一定提升,所以只要是查询的方法,建议使用此数据 如果为 false(默认值),事务需要提交的事务.建议新增,删除,修改.
propagation 控制事务传播行为: 当一个具有事务控制的方法 被另一个具有事务控制的方法调用的时候,需要如何管理事务(新建事务?事务挂起?报异常?)
REQUIRED (默认值): 如果当前有事务,就在事务中执行,如果当前没有事务,新建一个事务
SUPPORTS:如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行
MANDATORY:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错
REQUIRES_NEW:必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起
NOT_SUPPORTED:必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起
NEVER:必须在非事务状态下执行,如果当前没有事务,正常执行, 如果当前有事务,报错
NESTED:必须在事务状态下执行.如果没有事务,新建事务,如果当前有事务,创建一个嵌套事务
isolation=”” 事务隔离级别 在多线程或并发访问下如何保证访问到的数据具有完整性的
脏读:一个事务(A)读取到另一个事务(B)中未提交的数据,另一个事务中数据可能进行了改变,此时 A事务读取的数据可能和数据库中数据是不一致的,此时认为数据是脏数据,读取脏数据过程叫做脏读.
不可重复读:主要针对的是某行数据.(或行中某列) 主要针对的操作是修改操作 两次读取在同一个事务内 当事务 A 第一次读取事务后,事务 B 对事务 A 读取的数据进行修改,事务 A 中再次读取的数据和之前读取的数 据不一致,过程不可重复读
幻读:主要针对的操作是新增或删除 两次事务的结果 事务 A 按照特定条件查询出结果,事务 B 新增了一条符合条件的数据.事务 A 中查询的数据和数据库中的数据不一致的, 事务 A 好像出现了幻觉,这种情况称为幻读
DEFAULT: 默认值,由底层数据库自动判断应该使用什么隔离界别
READ_UNCOMMITTED: 可以读取未提交数据,可能出现脏读,不重复读,幻读,但效率最高
READ_COMMITTED:只能读取其他事务已提交数据.可以防止脏读,可能出现不可重复读和幻读 Oracle默认为此种事务隔离级别
REPEATABLE_READ: 读取的数据被添加锁,防止其他事务修改此数据,可以防止不可重复读.脏读,可能出现幻读 Mysql默认为此种事务隔离级别
SERIALIZABLE(最安全,效率最低): 排队操作,对整个表添加锁.一个事务在操作数据时,另一个事务等待事务操作完成后才能 操作这个表
rollback-for=”异常类型全限定路径” 当出现什么异常时需要进行回滚 建议:给定该属性值 ,手动抛异常一定要给该属性值 no-rollback-for=””当出现什么异常时不滚回事务
Spring中常用注解
@Component 创建类对象,相当于配置
@Service 与@Component 功能相同. 写在 ServiceImpl 类上
@Repository 与@Component 功能相同 写在数据访问层类上.
@Controller 与@Component 功能相同 写在控制器类上.
@Autowired(不需要写对象的 get/set) 默认按照 byType 注入
@Resource(不需要写对象的 get/set) 默认按照 byName 注入,如果没有名称对象,按照 byType 注入
@Value() 获取 properties 文件中内容
@Pointcut() 定义切点
@Aspect() 定义切面类
@Before() 前置通知
@After 后置通知
@AfterReturning 后置通知,必须切点正确执行
@AfterThrowing 异常通知
@Arround 环绕通知