spring学习笔记
spring全家桶
- spring,springmvc,spring boot,spring cloud
- spring:出现在2002年,解决了企业开发的难度,减轻了项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系,spring核心技术ioc,aop。能实现模块之间,类之间的解耦合
sping配置
-
加入maven依赖
<dependency> <groupId>org.spingframework</groupId> <artifactId>sping-context</artifactId> <version>5.3.5.RELEASE</version> </dependency>
-
主配置文件
<!--这个xsd和mybatis的dtd一样,是一种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"> </beans>
配置文件语法
<!--
告诉spring创建对象
声明bean,就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值,spring通过这个名称找到类
class:类的全限定名,不能是接口,因为spring是通过反射机制创建对象必须是类
class:类的全限定名,不能是接口,因为spring是通过反射机制创建对象必须是类
spring就完成new这个对象的任务
spring把创建好的对象放在map中,spring框架有一个map存放对象
例如:springMap.put(id的值,对象)
一个bean声明一个对象
创建对象的时间就是在创建spring容器中,会创建配置文件中的所有对象
spring默认调用的是类的无参构造方法
-->
<bean id="someService" class="com.liucheng.service.impl.SomeServiceImpl" />
<!--
name:表示构造方法的形参名
index:表示构造方法的参数的位置,从左到右0,1,2
ref:构造方法的形参类型是引用型,使用ref
-->
<bean id="" class="">
<constructor-arg name="" value="" />
<constructor-arg name="" value="" />
</bean>
String config="beans.xml";
ApplicationContext context=new ClassPathXmlApplicationContext(config);
//通过id取得对象
Object object=context.getBean(String id);
//获取容器中对象的数量
int count=context.getBeanDefinitionCount();
//获取容器中每个定义的对象名称(id值)
String[] names=context.getBeanDefinitionNames();
di:依赖注入
di的实现两种方法
- 在spring的配置文件中,使用标签和属性完成,叫做基于XML的di实现
- 使用spring中的注解,完成属性的赋值,叫做基于注解的id实现
di的语法分类
- set注入(设置注入):spring调用类的set方法,在set方法可以实现属性的赋值。80左右都是使用的set注入
- 构造注入,spring调用类的有参构造方法,创建对象,在构造方法中完成赋值
set注入
<!--必须要有set方法,set注入就是使用类的set方法赋值-->
<!--简单类型set赋值-->
<bean id="xx" class="[类全限定名]">
<property name="属性名称" value="属性值" />
<property name="属性名称" value="属性值" />
</bean>
<!--引用类型赋值-->
<bean id="xxx" class="[类全限定名]">
<property name="属性名称" ref="[其他bean的id]" />
</bean>
构造注入
<!--必须要有有参构造方法-->
<!--一个construction-age标签表示构造方法的一个参数-->
<bean id="" class="">
<constructor-arg name="[形参名]" value="[值]" />
<constructor-arg name="" ref="[其他bean的id]" />
<constructor-arg index="2" value="" />
</bean>
自动注入by name
当一个类引用类型属性的名称与对应类在配置文件的id相同就可以自动赋值
<bean id="xx" class="[类全限定名]" autowire="byName">
<property name="属性名称" value="属性值" />
<!--<property name="school" value="属性值" />-->
</bean>
<!--引用类型-->
<bean id="school" class="[类全限定名]">
<property name="属性名称" value="" />
</bean>
自动注入by type
<bean id="xx" class="[类全限定名]" autowire="byType">
<property name="属性名称" value="属性值" />
<!--<property name="school" value="属性值" /> 其中school类全限定名是com.liucheng.school-->
</bean>
<!--1.引用类型-->
<bean id="myschool" class="com.lilucheng.school">
<property name="属性名称" ref="[其他bean的id]" />
</bean>
<!--2.引用类型的子类,可以成功赋值-->
<bean id="schoolSon" class="com.liucehng.primarySchool" >
</bean>
<!--3.接口与实现类,也可以成功赋值-->
<!--以上三类要保证符合条件的只有一个,有多个符合会报错-->
多配置文件
<!--主配置文件引用其他配置文件,不同文件间自动注入也是可以的+-->
<beans>
<import resource="classpath:com.liucheng.spring-a.xml" />
<import resource="classpath:com.liucheng.spring-b.xml" />
<import resource="classpath:bao1,bao2" />
</beans>
引入资源文件
<context:property-placeholder location="classpath:url_zh_CN.properties" />
注解di注入
注解名 |
---|
@Component |
@Repository |
@Service |
@controller |
@Value |
@Autowired |
@Resource |
Component
Controller 控制器
Respository dao类
Service service类
Value(“xxx”)
Autowired
Resource
使用注解要求
<!--使用这个要在<beans>中声明组件(component)扫描器-->
<!--会扫描指定包及其子包中的所有类,找到类的注解,按照注解进行操作-->
<context:component-scan vase-package="[包名]" />
<!--还要加入新的约束文件-->
<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
https://www.springframework.org/schema/context/spring-context.xsd">
引入包的多种方式
<context:component-scan base-package="bao1" />
<context:component-scan base-package="bao2" />
<context:component-scan base-package="bao1;bao2" />
@Component
//不指定id名时,默认给的id名是类名首字母小写
@Component(value="myschool") or @Component("myschool") or @Component
public class Myshool{}
//相当于
<bean id="myschool" class="[类的全限定名]" />;
@Repository、@Service and @Controller
- 作用与Component相似
- @Repository是作用于持久层类上面:放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
- @Service是最庸于业务层类上面的:放在service的实现类上面,表示创建service对象,可以有事务等功能的
- @Controller是用于控制层类上面的:放在控制器的上面,表示创建控制器对象,能够接受用户提交的参数,显示请求的处理结果
@Value
//只能给基本类型和String类型赋值
@Value(value="3")
private int a;
@Value("3")
public void setA(int a){}
@Autowired
- 用自动注入来给引用类型赋值
- 默认是byType方式赋值
//byType
@Autowired
private School school;
//byName
@Autowired
@Qualifier(value="[id名]")
private School school;
- 属性required
//默认是true,表示如果赋值失败,程序报错
@Autowired
//如果是假,不报错,程序继续执行
@Autowired(required=false)
@Resource
//默认byName,如果不行就是用byType
@Resource
//只使用byName
@Resource(name="[id名]")
spring用aspectJ框架实现aop(动态代理)
aop的术语
- Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能。非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证
- JoinPoint:连接点,连接业务方法和切面的位置。
- Pointcut:切入点,指多个连接点方法的集合
- 目标对象:给哪个方法增加功能,这个类就是目标对象
- Advice:通知,通知表示切面功能能执行的时间,实在业务方法的前面还是后面
maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
execution表达式
- AspectJ定义了专门的表达式用于指定Pointcut
符号 | 描述 |
---|---|
* | 0至多个任意字符 |
… | 方法参数中:表示任意多个参数;包名后:表示当前包及其子包 |
+ | 类名后:表示当前类及其子类;接口后:表示当前接口及其实现类 |
//访问权限、包名与类名、抛出异常类型可以不写
execution(访问权限,方法返回值,包名.类名.方法名(方法参数),抛出异常类型);
//例子
//任意公共的方法
execution(public * *(..));
//任何一个以set开头的方法
execution(* set*(..));
//service包下的任意类任意返回值的任意方法
execution(* com.xyz.service.*.*(..));
//service包及其子包下的任意类任意返回值的任意方法
execution(* com.xyz.service..*.*(..));
//任意包的service子包下的任意类任意返回值的任意方法
execution(* *..service.*.*(..))
注解实现
- 方法注解
注解名称 | 描述 |
---|---|
@Before | 在目标方法之前执行 |
@AfterReturning | 在目标方法之后执行,能获取目标方法的返回值,处理这个返回值 |
@Around | |
@AfterThrowing | |
@After |
<!--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"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--声明目标对象-->
<bean id="student" class="com.liucheng.bean01.Student">
<constructor-arg name="name" value="三国" />
<constructor-arg index="1" value="2" />
</bean>
<!--声明切面类对象-->
<bean id="myAspect" class="com.liucheng.MyAspect" />
<!--
声明自动代理生成器
aspectj-autoproxy:会把spring容器中所有的目标对象,一次性生产代理对象
所以要放在目标类和切面类的声明后面
-->
<aop:aspectj-autoproxy />
</beans>
注解详解
Before and After
/**
* Aspect:是Aspectj框架中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类的上面
*/
@Aspect
public class MyAspect {
/**
* 定义方法:方法是实现切面功能
* 方法的定义要求:
* 1.公共方法 public
* 2.方法没有返回值
* 3.方法名称可以自定义
* 4.方法可以有参数,也可以没有参数
* 如果有参数,参数也不是自定义的,有几个参数可以用
* 1. JoinPoint jp
*/
//例如:@Before(value="execution(public void com.liucheng.bean01.Student.doSome(String,Integer))")
@Before(value="通过execution表达式得到的业务方法")
public void myAspect(){}
}
//After比AfterReturning还要靠后,一般用于资源清除
//总是会执行,就算目标函数有异常也会执行
//相当于try cache里的finally
@AfterReturning
@AfterReturning(value="execution()" returning="[返回值名]")
public void myAfterReturning(Object [返回值名]){}
@Around
/**
* 环绕通知方法的定义格式
* 1.public
* 2.必须有返回值,推荐Object
* 3.方法有参数,固定的参数ProceedingJoinPoint
* Around:环绕通知
* 属性:value 切入点表达式
* 特点:
* 1. 他是功能最强的通知
* 2. 在目标方法的前面和后面都能曾强
* 3.控制目标方法是否被调用
* 4.修改原来目标方法的返回值,影响最后的调用结果
*
* 环绕通知,等同于jdk动态代理的InvocationHandler接口
* 参数:ProceedingJoinPoint等同于Method,用来执行目标方法的
* 返回值:就是目标指向的执行结果,可以被修改
*/
@Around(value = "execution(public * com.liucheng.bean01.Student.doAround())")
public Object MyAround(ProceedingJoinPoint pjp) throws Throwable {
Object result=null;
//可以把pjp(ProceedingJoinPrint extends JoinPoint)当做JoinPoint执行各种方法
Object[] args = pjp.getArgs();
//目标方法调用
result=pjp.proceed();
//加入功能
result=(int)result+3;
//返回修改后的结果
return result;
}
参数详解
JoinPoint
//多个参数时,JoinPoint必须位于参数列表的第一个
public void myAspect(JoinPoint jp){
jp.getSignature(); //获得方法的签名(定义)
//void com.liucheng.bean01.Student.doSome(String,int)
String jp.getSignature().getName();
//doSame()
Object[] jp.getArgs();
//获得调用doSome方法使用的参数数组
}
Object [返回值名]
public void myAspect(Object ret){
//ret传入的是引用地址,修改基础类型和String并不会改变
//如果是引用型数据,改变值内属性就会改变返回值
}
execution表达式别名
//使用@PointCut来给表达式取别名
@PointCut(value="execution()")
public void myName(){}
@Before(value="myName()")
public void myAspect(){
}
动态代理的方式
- spring在默认情况下,有接口实现的类的aop动态代理使用的是jdk动态代理
- 没有接口实现的类使用的是cglib代理,cglib代理要求类能够继承
- 如果没有接口实现也想要使用cglib代理
<aop:aspectj-autoproxy proxy-target-class="true" />
spring实现mybatis
mybatis相关
<!--在大项目中mybatis提供的连接池POOLED并不是很好,我们平时不会用这种方式-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/liucheng?useUnicode=true;characterEncoding=utf8;serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="1727894442"/>
</dataSource>
我们使用独立的连接池类替换mybatis默认的,把连接池类交给spring创建
-
我们让spring创建以下对象
- 独立的连接池类对象,使用阿里的druid连接池
- SqlSessionFactory对象
- 创建dao对象
-
需要学习的就是上面三个对象的创建语法,使用xml的bean标签
总体过程
- maven依赖
- spring依赖
- mybatis依赖
- mysql驱动依赖
- spring的事务的依赖
- mybatis和spring集成依赖,mybatis官方提供的,用来在spring项目中创建mybatis的SqlSessionFatory,dao对象
- 创建实体类
- 创建dao接口和mapper文件
- 创建mybatis主配置文件
- 创建service接口和实体类,属性是dao
- 创建spring的配置文件:声明mybatis的对象交给spring创建
- 数据源
- SqlSessionFactory对象
- dao对象
- 声明自定义的service
- 创建测试类,获取Service对象,通过service调用dao完成数据库的访问
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
配置文件
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/liucheng?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT" />
<property name="username" value="root" />
<property name="password" value="1727894442" />
<property name="maxActive" value="20" />
</bean>
<!--创建SqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池交付给dataSource属性-->
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--
创建dao对象,使用了SqlSession对象,使用getMapper(dao.class)方法
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--把SqlSessionFactory对象的id给他创建SqlSession对象-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--把dao接口的包名给他,每个接口都执行一次getMapper()方法,获得每个接口的代理对象
创建好的dao对象放入到spring的容器中,id是类名首字母小写
-->
<property name="basePackage" value="com.liucheng.Dao" />
</bean>
public void test01(){
String config="applicationContext.xml";
ApplicationContext context=new ClassPathXmlApplicationContext(config);
String[] names = context.getBeanDefinitionNames();
StudentDao dao = (StudentDao) context.getBean("studentDao");
List<Student> students = dao.selectStudents();
students.forEach(System.out::println);
}
事务处理注解
@Transactional
- 通过此注解将事务织入到相应的public方法之中,实现事务管理
属性 | 默认值 | 描述 |
---|---|---|
propagation | Propagation.REQUIRED | 用于设置事务传播属性,值类型为Propagation枚举 |
isolation | Isolation.DEFAULT | 用于设置事务的隔离级别,值类型为Isolation枚举 |
readOnly | false | 用于设置该方法对数据库操作是否是只读(只查询) |
timeout | -1(没有时限) | 设置本操作与数据库连接的超时时限,单位秒,类型int |
rollbackFor | 空数组 | 指定需要回滚的异常类,类型为Class[](只有一个可以不使用数组) |
rollbackForClassName | 空数组 | 同上,不过类型为String[] |
noRollbackFor | 空数组 | 指定不需要回滚的异常类,类型为Class[](只有一个可以不使用数组) |
noRollbackForClassName | 空数组 | 同上,不过类型为String[] |
步骤
中小型项目
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库,指定数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象-->
<tx:annotation-driven transaction-manager="transactionManager" />
/**
* 出现异常时
* 1.spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中,在,就不管什么类型异常,一定rollback
* 2.若不在rollbackFor属性值中,判断是否是RunTimeException,是就rollback,不是就不。
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,RuntimeException.class
}
)
内部机制就是使用了@Around环绕通知
try{
要进行事务的方法();
spring事务管理.commit();
}cache(){
spring事务管理.rollback();
}
大型项目
<!--加入spring-aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库,指定数据源-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--声明业务方法他的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称,表示标签间的配置内容
transaction-manager:事务管理器对象的id
-->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager" >
<!--配置事务属性-->
<tx:attributes>
<!--给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
name:方法名称,1)完整的方法名称,不带有包和类
2)方法可以使用通配符,*表示任意字符
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="b*" propagation="REQUIRED" isolation="DEFAULT" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联advice和pointcut-->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="servicePt" />
</aop:config>
Web项目获得ApplicationContext对象
- 使用spring给我们的Context监听器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 把.xml配置文件路径给spring
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- java文件获得ServletContext里的ApplicationContext对象
WebApplicationContext context=null;
context=WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());