文章目录
- 跳转链接(学习路线)及前言(更新中)
- 快速入门
- 配置文件详解
- 依赖注入(bean实例化)
- 自动装配
- 集合注入
- 使用spring加载properties文件
- 容器
- 注解开发
- bean管理
- 注解开发依赖注入
- 第三方bean
- 整合mybatis
- 整合junit
- AOP
- 入门案例
- 切入点表达式
- 通知类型
- 获取数据
- 事务
- 事务角色
- 事务属性
跳转链接(学习路线)及前言(更新中)
后端
Java
↓
MySql
↓
jdbc
↓
javaEE/javaSE
↓
Tomcat
↓
Servlet
↓
[JSP]
↓
Spring
↓
SpringMVC
↓
SpringBoot
↓
SpringCloud
↓
zookeeper、kafka、ActiveMQ、RabbitMQ、RocketMQ、Lucene
增强
设计模式、数据结构与算法、Docker、Linux
前端
html
↓
css
↓
javascript
↓
javascriptDOM操作
↓
JQuery
↓
ajax
↓
vue
↓
react
安卓
嵌入式
快速入门
引入maven依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
创建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>
添加配置项
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl"></bean>
创建一个main方法
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
配置文件详解
bean范围配置,scope属性
取值 | 说明 |
---|---|
singleton | 默认的、单例的 |
prototype | 多例的 |
request | WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中 |
session | WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中 |
global session | WEB项目中,应该在Portlet环境,如果没有Portlet环境那么globalSession相当于session |
销毁和初始化方法(生命周期)
在对应bean中创建init和destory方法,在xml配置这两个方法以用于初始化和销毁
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory"></bean>
ClassPathXmlApplicationContext类中有个方法,有个方法,registerShutdownHook()注册关闭钩子,关闭容器
如果大量配置这种方法会很麻烦,这里有一个简化的使用方法
public class UserService implements InitializingBean, DisposableBean {
private UserDao userDao;
// ...
@Override
public void destroy() throws Exception {
// ...
}
@Override
public void afterPropertiesSet() throws Exception {
// ...
}
}
别名配置
<bean id="userDao" name="dao" class="com.shuxin.dao.impl.UserDaoImpl"></bean>
依赖注入(bean实例化)
实例化的三种方式
1、无参构造方法实例化
也就是默认的方法,上面使用的都是
2、静态工厂实例化(了解)
在这里,静态类,接口和静态工厂是等价的
需要一个工厂类,工厂类必须有静态方法,并且返回一个需要被实例化的类
public class StaticFactory {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置文件写工厂类
<bean id="factory" class="com.shuxin.factory.StaticFactory"></bean>
<bean id="userDao" class="com.shuxin.factory.StaticFactory" factory-method="getUserDao"></bean>
<!-- 那个类是工厂类? 那个方法是造对象的? -->
3、实例工厂实例化bean(了解)
这里的getUserDao非静态的,所以需要创建工厂的实例(对象),再使用该实例来调用造对象的方法,来获取到返回值。
public class StaticFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
<bean id="factory" class="com.shuxin.factory.StaticFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>
<!-- 用哪个bean造对象? 用哪个方法造对象? -->
4、Factory(是第三种方式的完善版 掌握)
public class FactoryBean implements org.springframework.beans.factory.FactoryBean<UserDao> {
// 代替第三种方法,固定创建对象的方法的名字
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
//这是第三个方法,这里控制该对象是否为单例
@Override
public boolean isSingleton() {
// 这是单例的,false为多例的(非单例)
return true;
}
}
<!-- 这种方式配置bean,那么配置文件内就会变得简单 -->
<bean id="userDao" class="com.shuxin.factory.FactoryBean">
<!-- 这里填factory的名称 -->
将Dao层注入到Service层
setter方式
<!-- application.xml -->
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl">
</bean>
<bean id="userService" class="com.shuxin.service.UserService">
<property name="userDaolll" ref="userDao"></property>
</bean>
// UserDaoImpl.java
public class UserDaoImpl{
public void fun(){
System.out.println("这是UserDaoImpl内的fun方法");
}
}
// UserService.java
public class UserService{
private UserDao userDaolll;
public void fun(){
userDaolll.fun();
System.out.println("这是UserService内的fun方法");
}
//需要注意设置set方法
public void setUserDaolll(UserDao userDaolll) {
this.userDaolll = userDaolll;
}
}
构造器方式
public class UserService implements {
private UserDao userDao;
public UserService(UserDao userDao111) {
this.userDao = userDao111;
}
}
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl">
</bean>
<bean id="userService" class="com.shuxin.service.UserService">
<!-- <property name="userDaolll" ref="userDao"></property>-->
<constructor-arg name="userDao111" ref="userDao"></constructor-arg>
</bean>
自动装配
按类型装配
<bean id="userService" class="com.shuxin.service.UserService">
<constructor-arg type="int" value="18"></constructor-arg>
<constructor-arg type="java.lang.String" value="zhangsan"></constructor-arg>
</bean>
public class UserService {
private int age;
private String name;
public UserService(int age, String name) {
this.age = age;
this.name = name;
}
}
按参数位置装配
<bean id="userService" class="com.shuxin.service.UserService">
<constructor-arg index="0" value="18"></constructor-arg>
<constructor-arg index="1" value="zhangsan"></constructor-arg>
</bean>
public class UserService {
private int age;
private String name;
public UserService(int age, String name) {
this.age = age;
this.name = name;
}
}
强制性依赖使用构造器的方式,setter方法有概率不执行,所以必须初始化的类使用构造器的方式,可选的依赖使用stter注入。
倡导使用构造器,第三方框架大部分使用构造器注入方式,有必要时,使用两种,根据实际情况。
自己开发模块使用setter注入,因为不常写构造器。
自动装配
1、按类型(常用)
2、按名称
3、按构造方法
4、不启用自动装配
1、
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.shuxin.service.UserService" autowire="byType" />
public class UserService {
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
按类型匹配,该类型的bean必须唯一,并且可以不写被装配的id属性
<bean class="com.shuxin.dao.impl.UserDaoImpl" />
2、
<bean id="userDao" class="com.shuxin.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.shuxin.service.UserService" autowire="byName" />
public class UserService {
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
按照UserService类的成员变量userDao的名来匹配,(更准确的说是按照set方法后的这个名字匹配)
自动装配不能用于简单类型
自动装配的优先级低于setter注入和构造器注入
集合注入
<bean name="listTest" class="com.shuxin.demo.ListTest">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="list">
<list>
<value>shuxinxin</value>
<value>123</value>
<value>hello</value>
</list>
</property>
<property name="set">
<set>
<value>ssss</value>
<value>ssss</value>
<value>abc</value>
</set>
</property>
<property name="map">
<map>
<entry key="1" value="avbc"></entry>
<entry key="2" value="abc"></entry>
<entry key="3" value="shuxin"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="country">China</prop>
<prop key="country">beijing</prop>
<prop key="country">nanjing</prop>
</props>
</property>
</bean>
public class ListTest {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String>map;
private Properties properties;
public ListTest() {
}
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;
}
}
使用spring加载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"
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/beans/spring-context.xsd
">
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!-- system-properties-mode="NEVER"不加载系统属性 加载多个 推荐使用classpath:*.properties 使用第三方jar包的话,使用classpath*:*.properties(不仅从该工程读,还可以从依赖中读) -->
<context:property-placeholder location="jdbc.properties,jdbc2.properties,*.properties,classpath:*.properties" system-properties-mode="NEVER"></context:property-placeholder>
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/shuxin
jdbc.username=root
jdbc.password=root
容器
使用文件加载xml配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\data\\tomcat\\mysbtis1\\springioc\\src\\main\\resources\\applicationContext.xml");
按类型获取bean
UserService bean = ctx.getBean(UserService.class);
bean.fun();
注解开发
<?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
">
<context:component-scan base-package="com.shuxin.service"></context:component-scan>
</beans>
@Component("ComponentBean")
// 这里不指定名称的话,访问时使用类型来访问
public class ComponentBean {
public void save(){
System.out.println("ComponetBean");
}
}
纯注解
// 调用
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService bean = ctx.getBean(UserService.class);
bean.fun();
// 新建一个配置类
// 设定当前类为配置类
@Configuration
// 设定扫描路径 多个写法 @ComponentScan({"com.shuxin","com.xxx",...})
@ComponentScan("com.shuxin")
public class SpringConfig {
}
bean管理
作用范围
控制单例非单例
@Scope默认为单例
@Scope(“singleton”)单例
@Scope(“prototyoe”)非单例
生命周期
@Component
@Scope("prototype")
public class UserService {
@PostConstruct
public void init(){
}
@PreDestroy
public void destory(){
}
public void fun(){
System.out.println("sdfsdfsdf");
}
}
// pom.xml 高于11版本的jdk使用这个依赖
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
注解开发依赖注入
注解开发依赖注入(自动装配)
@Component
public class UserService {
@Autowired
private UserDao userDao;
public void fun(){
userDao.save();
}
}
// 如果有多个实现可以为其命名
@Repository("userDao1")
@Component("userDao1")
// 使用这个注解指定转配的实现类
@Qualifier("userDao1")
简单类型
@Value("xiadada")
private String name;
从外部properties取值
1、在配置类添加注解,引入配置文件
@PropertySource("classpath:value.properties")
name="xiadada"
2、使用时
@Value("${name}")
private String name;
第三方bean
//@Configuration
public class JdbcConfig {
// 获得管理的bean对象
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://127.0.0.1:3306/java");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
}
@Configuration
//@ComponentScan("com.shuxin.config")
@Import(JdbcConfig.class) // 支持数组
public class SpringConfig {
}
整合mybatis
MybatiesConfig。java
public class MybatiesConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeAliasesPackage("com.shuxin.pojo");
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.shuxin.dao");
return mapperScannerConfigurer;
}
}
SpringConfig。java
@Configuration
@ComponentScan("com.shuxin")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatiesConfig.class})
public class SpringConfig {
}
JdbcConfig。java
public class JdbcConfig {
@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
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
整合junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class TestSSS {
@Autowired
private BookService bookService;
@Test
public void testBookService(){
bookService.fun();
}
}
简单类型
引用类型
AOP
入门案例
1、导入坐标
aop的包默认导入(spring包的依赖)
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.20</version>
</dependency>
2、制作连接点方法
3、制作共性功能
4、定义切入点
5、绑定切入点与通知的关系(切面)
@Component
@Aspect
public class MyAop {
@Pointcut("execution(void com.shuxin.service.TestWan.fun())")
private void fun(){}
// 共性功能
@Before("fun()")
public void commonFun(){
System.out.println(System.currentTimeMillis());
}
}
@Configuration
@ComponentScan("com.shuxin")
@EnableAspectJAutoProxy
public class SpringConfig {
}
切入点表达式
execution(void com.shuxin.service.TestWan.fun())
动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
访问修饰符:public,private等,可以省略
返回值
包名
类/接口名
方法名
参数
异常名:方法定义中抛出指定异常,可以省略
“ * ” 单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.shuxin.*.UserService.find* (*) )
匹配com.shuxin包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
“ … ” 多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..)
匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法
专用于匹配子类类型
execution(* *..*Service+.*(..))
注意:
所有代码按照标准规范开发,否则以下技巧全部失效
描述切入点通常描述接口,而不描述实现类
访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
返回值类型对于增删改类使用精准类型,加速匹配,对于查询类使用“通配快速描述
包名书写尽量不使用 … 匹配,效率过低,常用做单个包描述匹配,或精准匹配
接口名/类名书写名称与模块相关的采用匹配,例如UserService书写成Service,绑定业务层接口名
方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy*,selectAll书写成selectAll
参数规则较为复杂,根据业务方法灵活调整
通常不使用异常作为匹配规则
通知类型
前置通知
@Before("fun()")
public void commonFun(){
System.out.println(System.currentTimeMillis());
}
后置通知
@After("fun()")
环绕通知(重点)
@Around("fun()")
public Object commonFun(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("在前执行");
Object ret = pjp.proceed();
System.out.println("在后执行");
return ret; //或者返回原来本该返回的返回值,这里是ret(proceed方法返回原来的值)
}
对有返回值的方法,需要在下面将return写上(会将return覆盖掉,在proceed方法返回原来的值)
注意
环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Obiect类型
原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
返回后通知(了解)
设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
@AfterReturning("fun()")
public void commonFun(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("不抛异常才会在末尾执行");
}
抛出异常后通知(了解)
设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
@AfterThrowing("fun()")
public void commonFun(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("抛异常才会在末尾执行");
}
获取数据
获取切入点参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知
ProceedJointPoint:适用于环绕通知
获取切入点方法返回值
返回后通知
环绕通知
获取切入点方法运行异常信息
抛出异常后通知
环绕通知
事务
在需要添加事务的方法上添加注解,注意最好放到接口上
@Transactional
public void transfer(int out,int in,Double money);
在jdbcConfig配置类添加
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
在SpringConfig配置类添加注解
@EnableTransactionManagement
public class SpringConfig {
}
事务角色
事务管理员
发起事务方,在spring中通常指代业务层开启事务方法
事务协调员
加入事务方,在spring中通常指代数据层方法,也可以是业务层方法