一、工作原理
内部最核心的就是IOC了,动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射,反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置文件来动态的创建对象,和调用对象里的方法的 。
Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象进行监督和控制(也就是 在调用这类对象的具体方法的前后去调用你指定的 模块)从而达到对一个模块扩充的功能。这些都是通过 配置类达到的。
Spring目的:就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的(Spring根据这些配置 内部通过反射去动态的组装对象)
要记住:Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
二、IOC
1)概念
控制权由对象本身转向容器;由容器根据配置文件去创建实例并创建各个实例之间的依赖关系。
IOC的三种实现机制(三种注入方式)
通过Setter方法、构造方法、接口注入
2)核心——BeanFactory
1、Spring中的BeanFactory和ApplicationContext有什么联系?
两者都是IOC容器的实现方式。BeanFactory是Spring框架最核心的接口,它提供了IoC容器的配置机制。ApplicationContext建立在BeanFactory之上,提供了更多面向应用的功能,包括对国际化和框架事件体系的支持。通常将BeanFactory称为IoC容器,而ApplicationContext称为应用上下文,前者更倾向于Spring本身,后者更倾向于开发者,因此被使用得更多。
2、Bean的生命周期(单例、BeanFactory&ApplicationContext)
①. SpringIOC容器找到关于Bean的定义并实例化该Bean
②. SpringIOC容器实例化的Bean进行配置,也就是IOC注入
③. 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
④. 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean);如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
⑤. 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法
⑥、入股这个Bean关联了InitializingBean接口,则调用其afterPropertiesSet方法
⑦. 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
⑧. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法
3、Bean的作用域
五种作用域中,request、session和globalsession三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的SpringApplicationContext环境。
4、使用注解配置Bean
①在spring配置文件中注解扫描
②可以用@Component、@Respository、@Service、@Controller注解来标注需要由springIOC容器进行对象托管的类。
三、AOP
1)概念
Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Aspect(切面): 是切入点和通知(引介)的结合
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象(要增强的类)
Weaving(织入):是把增强应用到目标的过程,把advice 应用到 target的过程
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
注意:
①一个连接点上可以应用不止一个切面
②一个切点可以匹配多个连接点
补充通知:
AspectJ 支持 5 种类型的通知注解:
– @Before: 前置通知, 在方法执行之前执行
– @After: 后置通知, 在方法执行之后执行 ,无论该方法是否出现异常
– @AfterRunning: 返回通知, 在方法返回结果之后执行,可以访问到方法的返回值
– @AfterThrowing: 异常通知, 在方法抛出异常之后,可以访问到异常对象,且可以指定在出现特点异常时再执行通知代码
– @Around: 环绕通知, 围绕着方法执行
1、需要携带ProceedingJoinPoint类型的参数
2、类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
3、必须有返回值,返回值即为目标方法的返回值
@Before(”pointCut”)
@After(”pointCut”)
@AfterReturning(value=”pointCut”,returning=”result”)
@ AfterThrowing(value=”pointCut”,throwing=”e”)
@Around(”pointCut”)
Public ObjectaroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getsignature().getName();
try{
//前置通知
//执行目标方法
result = pjd.proceed;
//返回通知
}catch(Throwable e){
//异常通知
throw newException(e);
}
//后置通知
return result;
}
2)AOP的实现方式有哪几种?如何选择?
JDK动态代理实现和cglib来实现
选择:
1. 如果目标对象实现了接口,默认情况下回采用JDK的动态代理实现AOP,也可以强制使用cglib实现AOP
2. 如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
3)JDK动态代理如何实现?
只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。步骤如下:
1. 定义一个实现接口InvocationHandler的类
2. 通过构造函数,注入被代理类
3. 实现invoke( Object proxy, Method method, Object[] args)方法
4. 在主函数中获得被代理类的类加载器
5. 使用Proxy.newProxyInstance()产生一个代理对象
6. 通过代理对象调用各种方法
4)cglib动态代理实现
针对类实现代理,对是否实现接口无要求。原理是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以被代理的类或方法最好不要声明为final类型。
1. 定义一个实现了MethodInterceptor接口的类
2. 实现其intercept()方法,在其中调用proxy.invokeSuper( )
5)静态代理和动态代理的区别
静态代理:自己编写创建代理类,然后再进行编译,在程序运行前,代理类的.class文件就已经存在了。
动态代理:在实现阶段不用关心代理谁,而在运行阶段(通过反射机制)才指定代理哪一个对象。
6)使用Spring AOP
①注解
1、加入aop相关jar包
2、定义aop schema、<aop:aspectj-autoproxy/>和注解扫描
当SpringIOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为与AspectJ切面匹配的Bean创建代理
3、编写切面类
a、一个一般的java类,在其中添加额外实现的功能
@order()优先级,值越小优先级越高
@AspectJ切面声明
@Component 声明Bean
b、配置切入点、通知,并在通知中访问连接细节:
可以在通知方法中添加JointPoint类型的参数,从中可以访问到方法的签名和方法的参数
@Pointcut("execution(publicint com.yuhui.aop.ArithmeticCalculator.*(..))")
public voiddeclareJointPointExpression(){}
@Before("declareJointPointExpression()")
public voidbeforeMethod(JoinPoint joinPoint){}
②配置
1、加入aop相关jar包
2、定义aop schema
3、所有的springAop配置都必须定义在<aop:config>元素内部,对于每一个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端的实例
<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcutexpression="execution(* com.yuhui.aop.xml.ArithmeticCalculator.*(int,int))"
id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspectref="loggingAspect" order="2">
<aop:beforemethod="beforeMethod" pointcut-ref="pointcut"/>
<aop:after method="afterMethod"pointcut-ref="pointcut"/>
<aop:after-throwingmethod="afterThrowing" pointcut-ref="pointcut"throwing="e"/>
<aop:after-returningmethod="afterReturning" pointcut-ref="pointcut"returning="result"/>
<!--
<aop:around method="aroundMethod"pointcut-ref="pointcut"/>
-->
</aop:aspect>
<aop:aspectref="vlidationAspect" order="1">
<aop:beforemethod="validateArgs" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
四、事务
1)概念
Spring 既支持编程式事务管理, 也支持声明式的事务管理.
编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚.
声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. Spring 通过 Spring AOP 框架支持声明式事务管理.本质是对方法前后进行拦截,然后再目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务管理也有两种常用的方式:一种是基于tx和aop命名空间的xml配置;一种是基于@Transaction注解
①什么是事务
作为单个逻辑工作单元执行的一系列操作,满足四大特性:
- 原子性(Atomicity):事务作为一个整体被执行 ,要么全部执行,要么全部不执行;
- 一致性(Consistency):保证数据库状态从一个一致状态转变为另一个一致状态;
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行;
- 持久性(Durability):一个事务一旦提交,对数据库的修改应该永久保存。
②并发事务导致的问题
1. 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。
2. 脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。
3.不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。
4.幻读(phantom read):一个事务执行两次查询,第二次查询比第一次多出或少一些数据,造成两次结果不一致。只是另一个事务在这两次查询中间插入或者删除了数据造成的。
5. 第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失
③事务的隔离级别
1. Serializable 串行化:可避免脏读、不可重复读、幻读的发生
2. Repeatable Read 重复读:可避免脏读、幻读的发生
3. Read Commited 读已提交:可避免脏读的发生
4. Read Uncommited 读未提交:最低级别,任何情况都无法保证
④事务的传播属性
⑤事务其他属性
回滚事务属性:指定回滚规则. 如果有不止一种异常, 用逗号分隔.
超时事务属性: 事务在强制回滚之前可以保持多久. 这样可以防止长期运行的事务占用资源.
只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务
⑥事务管理器
事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中
:在应用程序中只需要处理一个数据源, 而且通过 JDBC 存取
: 在 JavaEE 应用服务器上用 JTA(JavaTransaction API) 进行事务管理
:用 Hibernate 框架存取数据库
2)使用spring事务
①配置
1、定义tx schema
2、注解扫描、导入资源文件
3、配置数据源
4、配置事务管理器
5、配置事务属性
6、配置事务切入点以及把书屋切入点和事务属性关联起来
2、<context:component-scanbase-package="com.yuhui.aop"></context:component-scan>
<context:property-placeholderlocation="classpath:db.properties"/>
3、<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user"value="${jdbc.user}"></property>
<property name="password"value="${jdbc.password}"></property>
<property name="jdbcUrl"value="${jdbc.jdbcUrl}"></property>
<propertyname="driverClass"value="${jdbc.driverClass}"></property>
<propertyname="initialPoolSize"value="${jdbc.initPoolSize}"></property>
<propertyname="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
4、<beanid="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"ref="dataSource"></property>
</bean>
5、<tx:adviceid="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根据方法名指定事务的属性 -->
<tx:method name="find*"propagation="REQUIRES_NEW
Isolation=”READ_COMMITED”
rollback-for=”java.io.IOException,java.sql.SQLException”
no-rollback-for=”java.lang.Exception”
timeout=”30”
read-only="true"
"/>
</tx:attributes>
</tx:advice>
6、<aop:config>
<aop:pointcutexpression="execution(*com.atguigu.spring.hibernate.service.*.*(..))"
id="txPointcut"/>
<aop:advisoradvice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
②注解
在方法或者类级别上添加@Transactional注解,当把这个注解应用到类上时,这个类中的所有公共方法被定义成支持事务处理的
1、定义tx schema
2、注解扫描、导入资源文件
3、配置数据源
4、配置事务管理器
5、启用事务注解
6、在方法或类上使用注解
2、<context:component-scanbase-package="com.yuhui.aop"></context:component-scan>
<context:property-placeholderlocation="classpath:db.properties"/>
3、<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user"value="${jdbc.user}"></property>
<property name="password"value="${jdbc.password}"></property>
<property name="jdbcUrl"value="${jdbc.jdbcUrl}"></property>
<propertyname="driverClass"value="${jdbc.driverClass}"></property>
<propertyname="initialPoolSize"value="${jdbc.initPoolSize}"></property>
<propertyname="maxPoolSize"value="${jdbc.maxPoolSize}"></property>
</bean>
4、<beanid="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"ref="dataSource"></property>
</bean>
5、<tx:annotation-driventransaction-manager="transactionManager"/>
6、@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
rollbackFor={ IOException.class,SQLException.class},
noRollbackFor={UserAccountException.class},
readOnly=true,
timeout=30)
五、七大模块
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText和 POI。
Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
六、其他
数据连接池的工作机制是什么?
J2EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。