SSM框架笔记(自用)-- Spring
Spring Framework系统架构
Spring程序开发步骤
核心概念
IoC( Inversion of Control)控制反转
使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
Spring技术对Ioc思想进行了实现
Spring提供了一个容器,称为Ioc容器,用来充当Ioc思想中的外部。
IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean。
DI ( Dependency Injection)依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
**目标:**充分解耦
使用IoC容器管理bean (IoC)
在IoC容器内将有依赖关系的bean进行关系绑定(DI)
最终效果
使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
bean配置
创建Spring配置文件,配置对应类作为Spring管理的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">
<!-- 1、导入spring的坐标spring-context,对应版本是5.2.7.RELEASE-->
<!-- 2、配置bean-->
<!-- bean标签表示配置bean-->
<!-- id属性表示给bean起名字-->
<!-- class属性表示给bean定义类型-->
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
<bean id="bookService" name="service service2 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="dao"/>
</bean>
</beans>
注意事项:bean定义时id属性在同一个上下文中不能重复。
bean基础配置
类别 |
描述 |
名称 |
bean |
类型 |
标签 |
所属 |
beans标签 |
功能 |
定义Spring核心容器管理的对象 |
属性列表 |
id : bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一;class : bean的类型,即配置的bean的全路径类名 |
范例 |
bean id=“bookDao” class=“com.itheima.dao.imp1.BookDaoImpl” /> |
范例 |
bean id=“bookService” class=“com.itheima. service.impl.BookServiceImpl”> |
bean别名配置
类别 |
描述 |
名 称 |
name |
类 型 |
属性 |
所 属 |
bean标签 |
功 能 |
定义bean的别名,可定义多个,使用迹号(,)分号(;)空格()分隔 |
范 例 |
bean id=“bookDao” name=“dao bookDaoImpl” class=“com.itheima.dao.imp1.BookDaoImpl” /> |
范 例 |
bean name=“service,bookServiceImpl” class=“com.itheima. service.imp1.BookServiceImpl” /> |
注意事项:获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No bean named ‘bookServiceImpl’ available
bean作用范围配置
类别 |
描述 |
名 称 |
scope |
类 型 |
属性 |
所 属 |
bean标签 |
功 能 |
定义bean的作用范围,可选范围如下:singleton:单例(默认) prototype :非单例 |
范 例 |
bean id=“bookDao” class=“com.itheima.dao.imp1.BookDaoImpl” scope=“prototype”/> |
bean相关
Bean实例化三种方式
使用无参构造方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
工厂静态方法实例化
工厂的静态方法返回Bean实例
public class StaticFactoryBean {
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="userDao" class="com.itheima.factory.StaticFactoryBean" factory-method="createUserDao" />
工厂实例方法实例化
工厂的非静态方法返回Bean实例
public class DynamicFactoryBean {
public UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="factoryBean" class="com.itheima.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="factoryBean" factory-method="createUserDao"/>
bean的依赖注入
概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IoC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
分析
构造方法
(1)setter注入
引用数据类型
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
简单数据类型
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
集合数据类型
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--list集合注入-->
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
</bean>
</beans>
(2)构造器注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
依赖注入方式选择
1、强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现;
2、可选依赖使用setter注入进行,灵活性强;
3、Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨;
4、如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入;
5、实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入;
6、自己开发的模块推荐使用setter注入。
Spring的重点配置
创建容器
方式一:类路径加载配置文件
ApplicationContext ctx = new ClassPathXm1ApplicationContext ("applicationContext.xml");
方式二∶文件路径加载配置文件
ApplicationContext ctx = new FileSystemxm1ApplicationContext("D:\\applicationContext.xm1");
加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext ("bean1.xm1","bean2.xml");
容器相关
依赖注入相关
Spring注解开发
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
配置数据源
数据源(连接池)的开发步骤:
① 导入数据源c3p0和druid的坐标和数据库驱动坐标,步骤略。
② 创建数据源对象;
③ 设置数据源的基本连接数据;
④ 使用数据源获取连接资源和归还连接资源。
applicationContext.xml加载jdbc.properties配置文件获得连接信息。
首先,需要引入context命名空间和约束路径:
● 命名空间:xmlns:context=“http://www.springframework.org/schema/context”
● 约束路径:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
Spring容器加载properties文件
<context:property-placeholder location="classpath*:*.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
纯注解开发
使用一个添加了 @configuration的config配置类来进行配置。
普通配置类注入
原始注解
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<!--注解的组件扫描-->
<context:component-scan base-package="com.itheima"></context:componentscan>
- 使用@Component或@Repository标识UserDaoImpl需要Spring进行实例化。
//@Component("userDao")
@Repository("userDao") //@Component衍生注解
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("save running... ...");
}
}
- 使用@Compont或@Service标识UserServiceImpl需要Spring进行实例化。
- 使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入。
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
/*
@Autowired //按照数据类型从Spring容器中进行匹配的
@Qualifier("userDao") //是按照id值从容器中进行匹配的 但是注意@Qualifier要结合@Autowired一起使用
*/
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Value("注入普通数据")
private String str;
@Value("${jdbc.driver}")
private String driver;
@Override
public void save() {
System.out.println(str);
System.out.println(driver);
System.out.println("save running... ...");
}
}
//@Scope("prototype")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
//此处省略代码
}
- 使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法。
@PostConstruct
public void init(){
System.out.println("初始化方法....");
}
@PreDestroy
public void destroy(){
System.out.println("销毁方法.....");
}
Spring新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
注解 |
说明 |
@Configuration |
用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解 |
@ComponentScan |
用于指定Spring在初始化容器时要扫描的包。作用和在Spring 的xml 配置文件中的<context:component-scan base-package=“com.itheima” />一样 |
@Bean |
使用在方法上,标注将该方法的返回值存储到Spring 容器中 |
@PropertySource |
用于加载.properties文件中的配置 |
@lmport |
用于导入其他配置类 |
- @Configuration
@ComponentScan
@Import
@Configuration //标志该类是Spring的核心配置类
@ComponentScan("com.itheima")
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {
}
- @PropertySource
@value
@Bean
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration { //简单型依赖注入
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}
//引用型依赖注入
@Bean(name="dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
Spring整合MyBatis
MyBatis程序核心对象分析
整合MyBatis
Spring整合JUnit
整合步骤
① 导入spring集成Junit的坐标(spring5 及以上版本要求 junit 的版本必须是 4.12 及以上–>)
② 使用@Runwith注解替换原来的运行期
③ 使用@ContextConfiguration指定配置文件或配置类
④ 使用@Autowired注入需要测试的对象
⑤ 创建测试方法进行测试
代码实现
① xml配置文件导入spring和junit坐标,代码略。
② 使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
}
③ 使用@ContextConfiguration指定配置文件或配置类
@RunWith(SpringJUnit4ClassRunner.class)
//加载spring核心配置文件
//@ContextConfiguration(value = {"classpath:applicationContext.xml"})
//加载spring核心配置类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
}
④ 使用@Autowired注入需要测试的对象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;
}
⑤ 创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;
@Test
public void testUserService(){
userService.save();
}
}
面向切面编程AOP
简介
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
作用及优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强。
优势:减少重复代码,提高开发效率,并且便于维护。
核心概念
-
代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
-
连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
-
切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
-
在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
-
一个具体方法:com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
-
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
-
通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
-
切面(Aspect):描述通知与切入点的对应关系
-
目标对象(Target):被代理的原始对象成为目标对象
切入点表达式
表达式标准格式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
访问修饰符(描述通配符)
● 访问修饰符可以省略
● 返回值类型、包名、类名、方法名可以使用星号* 代表任意
● 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
● 参数列表可以使用两个点 …表示任意个数,任意类型的参数列表
例如:
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
AOP切入点表达式书写技巧
-
所有代码按照标准规范开发,否则以下技巧全部失效;
-
描述切入点通常描述接口,而不描述实现类;
-
访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述);
-
返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述;
-
包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配;
-
接口名/类名书写名称与模块相关的采用*匹配,
例如UserService书写成*Service,绑定业务层接口名
-
方法名书写以动词进行精准匹配﹐名词采用*配﹐
例如getByld书写成getBy*,selectAll书写成selectAll;
-
参数规则较为复杂,根据业务方法灵活调整;
-
通常不使用异常作为匹配规则。
基于注解的AOP开发
开发步骤
① 创建目标接口和目标类(内部有切点)
② 创建切面类(内部有增强方法)
③ 将目标类和切面类的对象创建权交给 spring
④ 在切面类中使用注解配置织入关系
⑤ 在配置文件中开启组件扫描和 AOP 的自动代理
⑥ 测试
代码实现
① 创建目标接口和目标类(内部有切点)
public interface TargetInterface {
public void save();
}
public class Target implements TargetInterface {
public void save() {
System.out.println("save running.....");
}
}
② 创建切面类(内部有增强方法)
public class MyAspect {
//前置增强方法
public void before(){
System.out.println("前置增强..........");
}
}
③ 将目标类和切面类的对象创建权交给 spring
@Component("target")
public class Target implements TargetInterface {
public void save() {
System.out.println("save running.....");
//int i = 1/0;
}
}
@Component("myAspect")
public class MyAspect {
public void before(){
System.out.println("前置增强..........");
}
}
④ 在切面类中使用注解配置织入关系
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
//配置前置通知
@Before("execution(* com.itheima.anno.*.*(..))")
public void before(){
System.out.println("前置增强..........");
}
}
⑤ 在配置文件中开启组件扫描和 AOP 的自动代理
<!--组件扫描-->
<context:component-scan base-package="com.itheima.anno"></context:component-scan>
<!--aop自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
⑥ 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
⑦ 测试结果
前置增强..........
save running.....
注解配置AOP
注解通知类型
名称 |
注解 |
说明 |
前置通知 |
@Before |
用于配置前置通知。指定增强的方法在切入点方法之前执行 |
后置通知 |
@AfterReturning |
用于配置后置通知。指定增强的方法在切入点方法之后执行 |
环绕通知 |
@Aronud |
用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 |
@AfterThrowing |
用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
最终通知 |
@After |
用于配置最终通知。无论增强方式执行是否有异常都会执行 |
切点表达式的抽取
在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。具体如下:
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
//Proceeding JoinPoint: 正在执行的连接点===切点
//@Around("execution(* com.itheima.anno.*.*(..))")
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强....");
return proceed;
}
//@After("execution(* com.itheima.anno.*.*(..))")
@After("MyAspect.pointcut()")
public void after(){
System.out.println("最终增强..........");
}
//定义切点表达式
@Pointcut("execution(* com.itheima.anno.*.*(..))")
public void pointcut(){}
}
@Around注意事项
1.环绕通知必须依赖形参ProceedingloinP?oint才能实现对原始方法的调用﹐进而实现原始方法调用前后同时添加通知;
2.通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行;
3.对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型;
4.原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Ooject;
5.由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象。
Spring事务
事务作用:在数据层保障一系列的数据库操作同成功同失败。
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败。
编程式事务控制相关对象
PlatformTransactionManager
Spring为了管理事务,提供了一个平台事务管理器PlatformTransactionManager:
方法 |
说明 |
Transactionstatus getTransaction (TransactionDefination defination) |
获取事务的状态信息 |
void commit (Transactionstatus status) |
提交事务 |
void rollback (Transactionstatus status) |
回滚事务 |
注意:
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,
例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
TransactionDefinition
TransactionDefinition 是事务的定义信息对象,里面有如下方法:
方法 |
说明 |
int getIsolationLevel() |
获得事务的隔离级别 |
int getPropogationBehavior ( ) |
获得事务的传播行为 |
int getTimeout () |
获得超时时间 |
boolean isReadOnly () |
是否只读 |
TransactionStatus
TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。
方法 |
说明 |
boolean hasSavepoint () |
是否存储回滚点 |
boolean isCompleted () |
事务是否完成 |
boolean isNewTransaction () |
是否是新事务 |
boolean isRollbackonly () |
事务是否回滚 |
事务角色
事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法。
事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法。
进行提交与回滚,事务(转账操作中:A钱-,B钱+)作为一个整体,一旦部分执行不成功,能够整个回滚,从而确保若:A-成功,B+失败后,A,B都将回滚,恢复原状态。
事务传播行为
事务传播行为:事务协调员对事务管理员所携带事务的处理态度。