Spring
提示:以下是本篇文章正文内容,下面案例可供参考
一、控制反转IOC
1.概念了解
1.控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代
码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。
2.依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行
完成。
2.第一个spring程序
I.spring依赖引入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
II.定义实体类
III.创建Spring配置文件
在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名
称为 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-beans.xsd 是约束文件 控制本配置文件可以出现的标签和属性-->
<!-- 声明bean 告诉spring创建对象
id: spring所创建对象的标识
class: 所需创建对象的类型,类的全限定名称
spring完成了 SomeService service = new SomeServiceImpl();
spring将出创建好的对象放入spring专门放置对象的Map中
springMap.put(bean的id,对象)
该bean创建可理解为: springMap.put(someService,new SomeServiceImpl())
-->
<bean id="someService" class="chong.jiang.service.impl.SomeServiceImpl" />
</beans>
IIII.ApplicationContext的获取及常用API
@Test
public void testAcApi(){
String config = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
System.out.println("容器中对象数量:" + ac.getBeanDefinitionCount());
for(String name : ac.getBeanDefinitionNames()){
System.out.println(name);
}
System.out.println((new SimpleDateFormat(("yy/MM/dd HH:mm")).format(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24))));
}
获取时对象时注意:
IIIII.使用spring创建的对象
@Test
public void testSpringCreateBean(){
//1.指定spring配置文件
String config = "applicationContext.xml";
//2.创建表示spring容器的对象ApplicationContext
//ClassPathXmlApplicationContext类路径下加载配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//3.从容器对象中获取对象,并转成对应的类型
SomeService someService = (SomeService)ac.getBean("someService");
someService.doSome();
}
3.基于xml的DI
I.注入分类
a.set注入
<bean id="student" class="chong.jiang.domain.bean.Student">
<property name="name" value="小江" /> <!--遇到name的值为xxx,就会调用xxxSet()方法,不管是否存在该属性,是否赋值-->
<property name="age" value="18" />
<property name="school" ref="school" /> <!--使用ref对引用类型赋值-->
</bean>
<bean id="school" class="chong.jiang.domain.bean.School">
<property name="name" value="工程学院" />
<property name="address" value="重庆" />
</bean>
b.构造注入
<bean id="myStudent" class="chong.jiang.domain.bean.Student">
<constructor-arg index="0" value="张三" />
<constructor-arg value="20" /> <!--默认为index="1"-->
<constructor-arg name="school" ref="school" />
</bean>
II.引用类型的自动注入
a.byName(按名称注入)
赋予与属性名相同的id标识的bean对象
b.byType(按类型注入)
赋予与该属性同源的bean对象
III.配置多个spring配置文件
<?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-beans.xsd 是约束文件 控制本配置文件可以出现的标签和属性-->
<!-- spring主配置文件 加载其他配置文件 resource="classpath:配置文件的类路径" 可以用通配符“*” 注意:不能加载主配置文件,加载时配置文件需要放在一个目录中-->
<import resource="classpath:spring-school.xml"/>
<import resource="classpath:spring-student.xml"/>
</beans>
3.基于注解的DI
I.使用注解创建bean
@spring中和@Component功能一致,创建对象的注解还有:
1.@Repository(用在持久层类的上面) : 放在dao的实现类上面
表示创建访问数据库的dao对象
2.@Service(用在业务层类的上面) : 放在service的实现类上面,
表示创建业务处理、事务处理的service对象
3.@Controller(用在控制器的上面) : 放在控制类的上面,创建控制器对象
表示创建接受用户提交参数、显示请求结果的控制器对象
a.在创建的类上使用@Component()注解
b.在 Spring 配置文件中配置组件扫描器
<!--声明组件扫描器(component-scan),组件就是java对象
base-package:指定注解在项目中的包名
spring会扫描遍历base-package指定的包及其子包中的所有类,找到类中的注解,并按照注解的功能创建对象或这给对象赋值
-->
<context:component-scan base-package="chong.jiang.domain"/>
c.使用创建的对象
II.配置多个包的扫描器
III.属性注入
a.简单类型属性赋值
@Value 简单类型属性赋值
位置: 1.在属性定义上赋值,无需set方法,建议使用
2. 在set方法上面使用
使用:
b.引用类型属性赋值
$1.使用Autowired(required=true)注解
@Autowired(默认使用byType自动注入) 引用类型属性自动赋值 支持byName,byType
位置: 1.在属性定义上赋值,无需set方法,建议使用
2. 在set方法上面使用
属性:required ,表示属性赋值失败后是否报错,并停止运行,默认为true
为假时,属性赋值失败后为null
若要使用byName的方式注入,需要在属性加入@Autowired之后
多加一个@Qualifier(value="bean的id值")
$2.使用Resource注解()
@Resource( 默认使用byName自动注入,如果赋值失败,再使用ByType) 引用类型属性自动赋值 支持byName,byType 来自于JDK中
位置: 1.在属性定义上赋值,无需set方法,建议使用
2. 在set方法上面使用
当只是用ByName方式时,再注解中使用name属性,指定bean对象的id值
IIII.属性配置文件与注解注入的配合使用
a.加载配置文件
b.使用${}占位符
IIIII.xml注入与注解注入的选择
代码易常改动时,使用xml注入
代码不常改动时,使用注解注入(使用较多)
二、面向AOP编程
AOP 底层,就是采用动态代理模式实现的。
1. AOP编程术语
I.切面(Aspect)
切面泛指交叉业务逻辑,事务处理、日志处理就可以理解为切面,实际就是对主业务逻辑的一种增强。
II.连接点(JoinPoint)
连接点指可以被切面织入的具体方法。final修饰的方法不能做连接点
III. 切入点(Pointcut)
一个或多个连接点的集合。
IIII. 目标对象(Target)
将要被增强的主业务逻辑的类的对象
IIIII. 通知(Advice)
知表示切面的执行时间,Advice 也叫增强。
2. AspectJ对AOP的实现
I.AspectJ 的通知类型
II.AspectJ 的切入点表达式
表达式原型:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
理解为:
表达式常用匹配符:
3. AspectJ的使用
I.引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
II.基本实现
a.定义接口与实现类
//接口
public interface SomeService {
void doSome(String name,Integer age);
}
//实现类
public class SomeServiceImpl implements SomeService{
@Override
public void doSome(String name, Integer age) {
System.out.println("=====目标方法doSome()执行======");
}
}
b.定义切面类
/*
@Aspect: 表示当前类表示切面类,用来给业务方法增加功能的类
*/
@Aspect
public class MyAspect {
/*
定义实现切面功能的方法
*/
/*
@Before : 前置通知注解
属性: value,是切入点表达式,表示切面功能的执行位置
在目标方法执行之前执行
*/
//方法权限修饰符 返回值类型 方法声明(参数类型,参数类型...) 无异常
@Before("execution(public void chong.jiang.ba01.SomeServiceImpl.doSome(String, Integer))")
public void myBefore(){
System.out.println("切面功能(前置通知):在目标方法执行之前输出执行时间:" + new Date());
}
}
c.声明目标对象与切面类对象
d.注册 AspectJ 的自动代理
e.使用目标对象的id获取成功的代理对象
III. 多种通知方法的使用
a.@Before 前置通知
public void myBefore(JoinPoint joinPoint){
/*
JoinPoint要加入切面功能的业务方法 由框架赋值,必须放在参数列表的第一位
可以在通知方法中获取方法执行时发信息,如方法名称,方法的实参
*/
//获取方法完整定义
System.out.println("方法签名(定义)=" + joinPoint.getSignature());
System.out.println("方法的名称=" + joinPoint.getSignature().getName());
//获取方法实参
Object[] args = joinPoint.getArgs();
for(Object arg : args){
System.out.println("参数=" + arg);
}
System.out.println("切面功能(前置通知):在目标方法执行之前输出执行时间:" + new Date());
}
执行结果:
b.@AfterReturning 后置通知
执行结果:
c.@Around 环绕通知
结果
d.@AfterThrowing 异常通知
e.@After 最终通知
f. @Pointcut 定义切入点
f. JDK动态代理与cglib动态代理自动切换
当代理的类没有实现接口时,spring自动使用cglib的动态代理(继承方式)
有实现接口时:
没有实现接口的代理:
有接口实现时,使用cglib代理
三、Spring集成Mybatis
spring配置文件:创建druid,sqlsessionfactory,dao对象
<!--把数据库的配置信息写在独立文件中
加载属性文件,也就是占位符的本地存储
-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--连接池对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="${jdbc.maxActive}" />
</bean>
<!-- 用mybatis提供的SqlSessionFactoryBean类,创建factory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--给factory配置连接池-->
<property name="dataSource" ref="dataSource"/>
<!--mybatis的主配置文件
configLocation 类型为Resource 用于读取配置文件
-->
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--属性sqlSessionFactoryBeanName的类型是String类型,所以这里用value-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到的每个dao对象都放入spring的容器中,多个包用","分隔
dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="chong.jiang.dao" />
</bean>
<bean id="studentService" class="chong.jiang.service.impl.StudentServiceImpl" >
<property name="studentDao" ref="studentDao"/>
</bean>
四、Spring统一事务管理
1.使用 Spring 的事务注解管理事务(中小型项目)
I.事务管理器接口(重点)
PlatformTransactionManager 接口有两个常用的实现类:
DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
II.spring事务回滚方式
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。
III.事务定义接口
a.事务隔离级别
MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
b.事务传播行为
PROPAGATION_REQUIRED 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。也是 Spring 默认的事务传播行为。
PROPAGATION_REQUIRES_NEW 指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。如执行查询方法时
PROPAGATION_SUPPORTS 总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
c.事务超时
IIII.开启spring事务处理的spring配置
<!-- 1.声明事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 2.开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
transaction-manager: 事务管理器对象
-->
<tx:annotation-driven transaction-manager="transactionManager" />
IIIII.注解使用
2.使用 AspectJ 的 AOP 配置管理事务(大型项目)
I.配置到spring.xml中的配置
<!-- 1.声明事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 2.声明业务方法的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称,表示该配置的标识
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<!---给具体的方法配置事务属性-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,chong.jiang.excep.NotEnoughException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--
配置切入点表达式,指定那些包中的类要使用事务
id:该个切入点表达式的标识
expression:切入点表达式,只当让aspectJ为那些类创建代理对象
-->
<aop:pointcut id="servicePt" expression="execution(* *..Service..*.*(..))"/>
<!--配置增强器:advice和pointcut
配置advice与pointcut的关联
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>
五、Spring与web
使用监听器创建spring容器
I.在maven中加入依赖
<!-- 为了使用监听器对象 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
II.注册监听器 ContextLoaderListener
在 web.xml 中注册该监听器
<!--注册监听器ContextLoaderListener
监听器对象被创建后,会读取/WEB-INF/applicationContext.xml
为了创建ApplicationConText对象,需要加载配置文件,spring默认加载路径为/WEB-INF/applicationContext.xml
-->
<context-param>
<!--从监听器 ContextLoaderListener 的父类 ContextLoader 的源码中可以看到其要读取的配
置文件位置参数名称 contextConfigLocation。-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Spring 为该监听器接口定义了一个实现类 ContextLoaderListener,完成了两个很重要的工作:创建容器对象,并将容器对象放入到了 ServletContext 的空间中。
III.获取Spring 容器对象
a.直接从 ServletContext 中获取
ApplicationContext ctx = null;
//从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中存放的 key 为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。
String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr = getServletContext().getAttribute(key);
if(attr != null){
ctx = (ApplicationContext)attr;
}
b.通过 WebApplicationContextUtils 获取
//使用框架方法获取容器对象
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());