若嫌排版太乱,请点击下载word版本(下载后调整到A3页大小)
Spring
Spring的简介
spring是一个开源的框架,较有效的解决了企业应用开发的复杂性 。它主要体现在松偶性和整合功能。
Spring就像一个交警,来指挥其中管理的对象的走向,而xml配置和注释就像其手中的指挥棒
在SSH框架中spring充当了管理容器的角色,在SSH框架中Hibernate用来做持久层,他对JDBC做了一个良好的封装,Struts用来做应用层,负责调用业务逻辑service层,所以SSH框架的流程大致是:Jsp页面----Struts----Service(业务逻辑处理类)---Hibernate,但是他们层与层之间属于直接调用的关系,耦合性太大,以后如果要换底层的代码,改动很麻烦,所以在这里spring的作用就体现了出来,spring作为容器,来管理struts和hibernate之间的工作。它其实就是程序中的一个桥梁或者说管理者,整个程序的运行都依靠spring来管理。
servlet层用来接收请求获取请求参数、调用service来处理获取数据,封装参数、请求转发重定向等。
service层是用来处理业务逻辑,对接收到的对象进行处理
IOC(DI)控制反转(Spring的两大核心之一)
-
IOC(DI)的介绍
IOC控制反转,DI依赖注入。
在原先我们要对象之间彼此调用协作时,都需要来new一个对象,进行调用,这样对象之间的耦合依赖性就很高了,而IOC的思想就是,通过Spring容器来实现各个对象之间的创建和协调工作,而每个对象只要专注于自己本身的逻辑就可以了。IOC是spring的核心,贯穿始终,负责控制对象的生命周期和组织对象之间的关系,
举个例子,我们平时找女朋友的时候,我们到处去看哪里有长的好看的妹子,然后去了解他们的兴趣爱好,qq好,电话等信息,把他们new到自己的对象中,然后...嘿嘿嘿....这个过程我们必须要自己去面对每个环节,在传统的程序中也是这样,如果向使用另一个对象,就必须先得到他,使用之后还要销毁,对象之间始终耦合在一起,
而IOC可以把它比喻成一个婚姻介绍所,介绍所中有很多男男女女的资料,我们要用的时候,就把我们的要求提供给他们,婚姻介绍所就会给我们找来一个mm,之后就可以谈恋爱,嘿嘿嘿了..如果找到的mm不符合我们的要求,我们就会抛异常。而spring所倡导的方式就是如此,把所有的类在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring就会在运行到适当的时候把你要的东西给你,同时也会把你交给其他需要你的东西。在之前对于具体的对象而言,以前是他控制其他对象,现在是所有的对象都被spring控制,所以这叫做控制反转。
IOC的一个重点是在系统运行时,动态的向某个对象提供他所需要的其他对象,只一点通过DI(依赖注入)实现。
-
IOC(DI) xml方式实现
-
2.1 IOC实现
- bean标签的配置
<beanid="user"class="Demo1.User">
<propertyname="username"value="李骐辰"></property>
</bean>
bean标签就是spring中一个对象的登记。(不仅是javabean,dao层实例也可以)
id属性:给bean起个名。项目唯一,不能有字符,之前用name属性,和id一样,但是可以使用特殊字符
class属性:对象类的权限定名。
scope属性: bean的作用范围,常用值 singleton:单例(默认)prototype:多例(action时要配置成多例的)
init-method ="方法名":初始化的时候执行的方法
destroy-method="方法名":销毁的时候执行的方法(单例模式下关闭工厂才会执行多里模式下是关闭不了的)
- 导入其他配置文件
(分模块用)
<import resource="applicationContext2.xml"/>
-
2.2 普通DI依赖注入
- ☆方式一:,配置文件通过属性set方式
<beanid="user"class="Demo1.User">
<propertyname="username"value="李骐辰"></property>
<propertyname="user1"ref="user2"></property>
</bean>
给类的属性提供set方式,通过property标签来注入
name属性:类中的属性明,value属性:要注入的值,ref属性:如果注入的是对象,用ref引用bean的id名
- 方式二:带参构造器的方式
bean要提供带参构造器
- 方式三:p名称空间方式注入,spring2.5之后提供
步骤:
1.在配置文件约束中导入p的名称空间
xmlns:p="http://www.springframework.org/schema/p"
2.bean标签中不用使用property注入
<bean id="" class="" p:属性名称="值" p:属性名称-ref="值"/>
例如:
<bean id="car2" class="cn.itcast.d_di.Car2" p:name="保时捷" p:price="1800000"/>
-
2.2 集合和文件DI依赖注入
- 数组和list
<propertyname="list属性名">
<list>
<value>11</value>
<value>22</value>
<value>33</value>
</list>
</property>
- properties文件
<propertyname="pp">
<props>
<propkey="password ">liqichen</prop>
<propkey="username">root</prop>
</props>
</property>
- map
<propertyname="map属性名">
<map>
<entrykey="11"value="aa"></entry>
<entrykey="22"value="bb"></entry>
<entrykey="33"value-ref="car2"></entry>
</map>
</property>
-
IOC的入门案例
第一步:下载jar包
第二步:新建web项目 (导入jar包和日志jar包)
第三步:编写一些类
第四步:编写配置文件(xml)
名称:自定义 一般叫做:applicationContext.xml
位置:自定义 一般放在src下
导入约束:bean的约束
在解压目录下 \spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\htm\xsd-configration.html
第五步:测试编码(创建工厂类,调用getbean获取指定的实现类即可)
创建一个工厂类,调用getBean获取指定的实现类即可
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao)context.getBean("userDao");
-
工厂类详解:
1.之前版本:BeanFactory
2.现在的版本:ApplictionContext (有两个实现类)
☆实现类一:ClassPathXmlApplictionContext (读取类路径下的Xml文件)
实现类二:FileSystemXmlApplictionContext (读取文件系统上的xml文件)(磁盘上的)
☆BeanFactory和ApplictionContext 关系
ApplictionContext是BeanFactory的子类,子类拥有更多的方法
他们两个的配置文件的bean的初始化时机不一样
ApplictionContext:配置文件加载完成的时候,完成bean的初始化
BeanFactory:是在调用getBean方法的时候完成bean的初始化
-
IOC(DI) 注解方式实现
-
5.1 注解实现bean的配置
- 简单实现
1.导包,需要导入6个主jar包和一个aop.jar
2.实现类的上边直接用注释配置,顶替xml的<bean id="" class="" >
@Component(value="userDao")//组件的意思
publicclassUserDaoImplimplements UserDao {
}
解释:因为注解在类中,所以直接给类起名就行,value就是bean标签的id属性
- 注解分类
@Component:声明该类交给spring管理
具有三个延伸的注解
@Controller 用在WEB层
@Sercvice 业务层
@Repository 持久层
这些注解的功能都是一样的,只是为了方便区分
- 5.2 DI(依赖注入)的注解
在bean中提供成员 (可以不提供set方法)在成员上添加注解
-
若成员是普通的类型添加以下
@Value(注入值)
-
若成员是其他的对象添加以下
@AutoWired :默认是按照类型注入
若是想按照名称注入需要配合@Qualifier("bean的名称")
☆在开发中经常使用@Resource
@Resource(name="bean的id") 使用名称注入
若注解没有使用name属性 回归到按照属性注入
- 5.3 其他的注解
☆@Scope:声明注解的范围 (singleton单利 prototype多例)
@PostConstruct 等价于 init-method
@PreDestroy 等价于 destroy-method属性
AOP 面向切面编程(Spring的两大核心之一)
1. AOP的介绍
aop是oop的延伸,提高程序的可重用性,同时提高了开发的效率。
是spring的核心之一.
应用场景:
权限的校验
性能的测试
事务的控制
日志的记录
底层:
动态代理技术(理解中了解)
-
动态代理
-
2.1 jdk的动态代理(要求:必须有接口)
-
步骤
1、创建一个动态代理类
2、给该类提供一个代理对象的私有成员属性
3、提供带参构造方法
4、编写动态代理方法 返回一个代理后的对象
- 代码实现
-
public UserDao createProxy(){
UserDaoproxy = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(), //第一个参数是被代理对象的类加载器
userDao.getClass().getInterfaces(), //第二个参数是被代理对象实现的接口
new InvocationHandler() { //第三个对象是
@Override
public Object invoke(Objectproxy, Methodmethod, Object[] args)throws Throwable {
//判断是否是save方法在原先的方法前面干什么
if("save".equals(method.getName())){
System.out.println("--before-");
}
//执行原来的操作
returnmethod.invoke(userDao,args);
}
});
returnproxy;
}
- 2.2 cglib的动态代理(不需要接口)
publicclass ProductDaoProxyFactory {
private ProductDaoproductDao;
public ProductDaoProxyFactory(ProductDaoproductDao) {
this.productDao =productDao;
}
public ProductDao createProxy(){
//创建的是该类的子类的代理对象
//1.创建一个核心对象
Enhancerenhancer =new Enhancer();
//2.设置父类
enhancer.setSuperclass(productDao.getClass());
//3.设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
//MethodProxy 方法的代理对象
public Object intercept(Objectproxy, Method method, Object[] args, MethodProxymethodProxy)throws Throwable {
//判断是否是save方法
if("save".equals(method.getName())){
System.out.println("--before-");
}
//执行原来的逻辑
returnmethodProxy.invokeSuper(proxy,args);
}
});
//4.创建代理对象
return (ProductDao)enhancer.create();
}
}
3. AOP的术语
joinPoint:连接点,可以加强的方法
pointcut:切入点,需要加强的方法
advice:通知/增强 ,加强的逻辑(代码)
target:需要增强的类 productDao
weaving:织入 将通知作用在某一个切入点的过程
proxy:代理对象, 织入完成之后产生的对象
aspect:切面 多个切入点和通知的组合
-
AOP的简单实现
-
4.1 代码实现
<!--aop配置 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcutid="test1"expression="execution(* Hello1.DaoImpl.save(..))"/>
<!-- 编写切面 -->
<aop:aspectref="MyAspect"> //ref引入的是类的引用,或者是自定义切面类(事物通知)
<!--通知:定义在哪个切入点之前使用切面类的哪个方法 -->
<aop:beforemethod="f"pointcut-ref="test1"/>
</aop:aspect>
</aop:config>
-
4.2 切入点表达式
execution函数引入
表达式格式 :
[方法的修饰符] 方法的返回值 包名.类名.方法名.(参数)
例如:
* Hello1.DaoImpl.save(..)指定包下的指定类的save方法 返回值是任意的参数任意
* Hello1.*.save(..)指定包下的任意类的save方法 返回值是任意的参数任意
* Hello1..*.save(..)指定包下的及其子包下的任意类的save方法 返回值是任意的参数任意
* Hello1.*Dao.save(..)指定包下的以Dao结尾的类的save方法 返回值是任意的参数任意
* Hello1.Dao+.save(..)指定包下的Dao类及其子类或实现类的save方法 返回值是任意的参数任意
-
通知(加强的逻辑)
位置:在xml中aop:aspect标签中的aop:before标签
前置通知: 在方法执行前 before
后置通知: 在方法执行后 after-returning (可以获取方法的返回值)
环绕通知: 在方法执行前后 around (可以组织目标方法的执行)
//环绕解决阻止原来的方法
publicvoid aroundMethod(ProceedingJoinPointjoinPoint)throws Throwable{
System.out.println("--around 前-");
//执行原来的逻辑
joinPoint.proceed();
System.out.println("--around 后-");
}
异常抛出通知: 在方法发生异常时 after-throwing (获取异常的信息)
//异常抛出通知
publicvoid afterThrowMethod(Throwabletr){
System.out.println("-- 异常抛出 -"+tr.getMessage());
}
<!-- 异常通知 -->
<aop:after-throwingmethod="afterThrowMethod"pointcut-ref="pointcut4"throwing="tr"/>
最终通知: 在目标方法执行之后无论如何都执行的 after
注意:无论哪种切入点 都可以获取切入点的信息 在切面类方法参数里传入 JoinPoint joinPoint
-
test整合
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ac2.xml")
publicclass TestTest {
//以前的写法
/*
@Test
public void t1(){
ApplicationContext context = new ClassPathXmlApplicationContext("ac2.xml");
CategoryDao categoryDao = (CategoryDao)context.getBean("categoryDao");
}*/
@Resource(name="categoryDao")
private CategoryDaocategoryDao;
@Test
publicvoid f(){
categoryDao.save();
categoryDao.update();
}
}
-
aop的注解方式
入门案例;
1.导入jar包
2.创建配置文件(导入约束)
3.创建目标类
4.创建切面类
5.配置配置文件
★在配置文件中 让aop自动代理
<aop:aspectj-autoproxy/>
把目标类和切面类放入spring中管理
<bean id="userDao" class="cn.itcast.a_hello.UserDao"></bean>
<bean id="myAspect" class="cn.itcast.a_hello.MyAspect"></bean>
6.在切面类直接写注释就行
类上的注释有
在切面类上的方法上加入注解:
@Before
@AfterReturning
@Around
@AfterThrowing
@After
注解(切入点)
-
springJdbc模板☆ (转账案例)
- 8.1 spring中的事物
PlatformTransactionManager:平台事物管理器
TransactionDefinition:事物定义
作用:
隔离级别
传播行为
事物是否只读
超时信息
TransactionStatus:事物状态 (事物产生的状态的记录)
☆:
spring使用事物的时候,先通过事物定义对象,对事物进行规定,使用平台事物管理器管理事物,产生的信息会记录到事物状态中
- 8.2 JDBC模板配置
作用:用来和数据库交互的,用法类似于DBUtils
入门案例:
-
导入jar包
6+驱动包+JDBC的包+事物TX的包+aop的包
-
第一种:纯手动设置的方式
publicvoid f(){
//spring内置连接池
DriverManagerDataSourcesource =new DriverManagerDataSource();
//设置四个参数
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///hibernate_day1");
source.setUsername("root");
source.setPassword("liqichen");
//创建spring的jdbc模板
JdbcTemplatetemplate =new JdbcTemplate() ;
//更改内置连接池
template.setDataSource(source);
//执行 设置参数
//template.update("insert","参数1","参数2...");
template.update("insert into cst_customer(cust_name) VALUES(?)","力气");
}
-
第二种:将数据源和jdbcTemplate的创建交给spring管理
在xml中配置数据源和jdbc模板
<!--配置内置数据源 -->
<beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<propertyname="driverClassName"value="com.mysql.jdbc.Driver"></property>
<propertyname="url"value="jdbc:mysql:///hibernate_day1"></property>
<propertyname="username"value="root"></property>
<propertyname="password"value="liqichen"></property>
</bean>
<!-- jdbcTemplate 配置JDBC模板-->
<beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
<!--name 属性名 固定dataSourceref 引用数据源 -->
<propertyname="dataSource"ref="dataSource"/>
</bean>
在使用的时候直接在类中创建一个模板成员之后再方法中直接调用方法就行
@Resource
private JdbcTemplatejdbcTemplate;
@Test
publicvoid t1(){
jdbcTemplate.update("insert into cst_customer(cust_name) VALUES(?)","11");
}
-
8.3 整合其他数据源
- 整合dbcp
-
导入jar包 (dbcp+pool 在依赖包里)
-
在配置文件的数据源的位置替换
<!-- 整合dbcp-->
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<propertyname="driverClassName"value="com.mysql.jdbc.Driver"/>
<propertyname="url"value="jdbc:mysql:///sday03"/>
<propertyname="username"value="root"/>
<propertyname="password"value="1234"/>
</bean>
- 整合c3p0☆
-
导入jar包(1个或2个)
在依赖包文件夹1个
-
在配置文件的数据源的位置替换
<!-- 整合 C3P0-->
<beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
<propertyname="driverClass"value="com.mysql.jdbc.Driver"/>
<propertyname="jdbcUrl"value="jdbc:mysql:///sday03"/>
<propertyname="user"value="root"/>
<propertyname="password"value="1234"/>
</bean>
-
8.4 抽取数据库基本信息到属性文件(properties)
-
新建一个properties文件
放在src下,编写4个基本信息。例
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///sday03
jdbc.username=root
jdbc.password=1234
-
加载属性文件
方式一:
方式二☆:
//使用这个标签导入地址
<context:property-placeholderlocation="classpath:jdbc.properties"/>
-
获取属性文件中的值
//数据源上用${文件名.属性名}来获取其中的属性
<!-- c3p0数据源 -->
<beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
<propertyname="driverClass"value="${jdbc.driverClass}"/>
<propertyname="jdbcUrl"value="${jdbc.url}"/>
<propertyname="user"value="${jdbc.username}"/>
<propertyname="password"value="${jdbc.password}"/>
</bean>
-
-
8.5 利用模板curd操作:
cud: 方法("sql","参数")
r:
- 将唯一的结果(一行一列)封装成指定类型
T queryForObject(string sql , class<T> clazz , object args)
Longl =jdbcTemplate.queryForObject("select count(*) from account", Long.class);
- 将唯一的记录封装成指定对象
T queryForObject(string sql ,RowMapper rowMapper , object args)
编写RowMapper
//结果集处理类 相当于 DBUtils中的ResultSetHandler
classMyRowMapperimplements RowMapper<Account>{
@Override
public Account mapRow(ResultSetrs,intindex)throws SQLException {
Accountaccount =new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getInt("money"));
returnaccount;
}
}
//查询语句
Accounta =jdbcTemplate.queryForObject("select * from account where id = ?",new MyRowMapper(),5);
- 将多个的记录封装成指定对象集合
List<T> query(string sql ,RowMapper rpw , object args)
List<Account>list =jdbcTemplate.query("select * from account",new MyRowMapper());
-
声明事物
- 方式1:手动编程式事务(了解)(以后不常用)
-
在配置文件中配置事务管理器
<!-- 配置事务管理器 -->
<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<propertyname="dataSource"ref="dataSource"></property>
</bean>
2.提供一个事务管理模版,配置事务管理器模版
<!-- 配置事务管理器模版-->
<beanid="transactionTemplate"class="org.springframework.transaction.support.TransactionTemplate">
<!-- 注入事务管理器 -->
<propertyname="transactionManager"ref="transactionManager"></property>
</bean>
-
在service中提供模版成员属性
privateTransactionTemplatetransactionTemplate;
publicvoid setTransactionTemplate(TransactionTemplatetransactionTemplate) {
this.transactionTemplate =transactionTemplate;
}
-
在service中注入模版
<!-- service -->
<beanid="accountService"class="cn.itcast.b_shoudong.AccountService">
<propertyname="accountDao"ref="accountDao"/>
<!-- 注入模版 -->
<propertyname="transactionTemplate"ref="transactionTemplate"></property>
</bean>
-
在service层通过java代码控制事务
publicvoid account(final String fromUser,final StringtoUser,finalintmoney){
//调用事物管理器模板成员(传入参数)
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protectedvoid doInTransactionWithoutResult(TransactionStatusarg0) {
//在这里写操作,算是事物的内部
accountDao.accountOut(fromUser,money);
inti = 1/0;
accountDao.accountIn(toUser,money);
}
});
}
- 方式2:声明式事物☆☆(必须掌握其中的一种)
每种方式都需要先配置事物管理器
<!-- 配置事务管理器 -->
<beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource"ref="dataSource"/>
</bean>
- 方式2.1:XML方式
-
配置事务通知(切面类)
<!-- 配置切面类(事务通知) 定义规则的 -->
//transaction-manager属性是引用事物管理器
<tx:advicetransaction-manager="transactionManager"id="txAdvice">
<tx:attributes>
<!--
//name属性:方法名,isolation属性:隔离级别,propagation属性:传播行为
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
-->
<tx:methodname="account"isolation="DEFAULT"propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
-
aop配置
<!-- aop配置 -->
<aop:config>
<!-- 定义切入点 -->
<!-- <aop:pointcut expression="execution(* cn.itcast.service..*.*(..))" id="pointcut"/> -->
<aop:pointcutexpression="execution(* cn.itcast.c_xml.AccountService.account(..))"id="pointcut"/>
<!-- 配置切面 -->
<aop:advisoradvice-ref="txAdvice"pointcut-ref="pointcut"/>
</aop:config>
- 方式2.2:注解方式
- 在配置文件中开启注解事务
<!-- 开启注解事务 -->
<tx:annotation-driven/>
2.在service类上添加注解
@Transactional
publicclass AccountService {
}