Spring
一.spring介绍
1.1 spring理念
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架,使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
1.2 Spring优点
- Spring是一个开源的免费框架
- Spring是一个轻量级的,非入侵式(引入spring不会改变原来的代码,不会对原来的项目进行影响)的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对各种框架整合的支持
但是发展了太久之后,违背了原来的理念,配置十分繁琐。
二.Spring入门
2.1创建一个Hello实体类
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
2.2 编写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">
<!--bean就是java对象,相当于new一个对象 由Spring创建和管理-->
<bean id="hello" class="com.study1.pojo.Hello">
<!-- property是给对象赋值 给属性name赋值为spring-->
<property name="name" value="Spring"/>
</bean>
</beans>
2.3测试
public class HelloTest {
public static void main(String[] args) {
//获取spring的上下文对象,解析beans.xml文件 , 生成管理相应的Bean对象,context也就是spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象都在spring中管理了,如果要使用,直接去里面读出来就可以
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
2.4 初步总结
- hello对象是由Spring创建的
- hello对象的属性是由spring容器(property)设置的
这个过程就叫控制反转
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
**依赖注入 : 就是利用set方法来进行注入的.**类中必要要有set方法
IOC是一种编程思想,由主动的编程变成被动的接收
2.5.IOC创建对象的方式
2.5.1.使用无参构造来创建对象(需要使用set)
<bean id="hello" class="com.study1.pojo.Hello">
<!-- property是给对象赋值 给属性name赋值为spring-->
<property name="name" value="Spring"/>
</bean>
如果是无参构造,可以不给属性赋值,但是有参构造必须赋值(且必须将有参构造中所有属性赋值),使用以下三种方法
2.5.2.使用下标赋值(使用有参构造方法)
<bean id="hello" class="com.study1.pojo.Hello">
<constructor-arg index="0" value="nihao"/> //0表示有参构造的第一个参数
</bean>
2.5.3 使用类型来赋值(使用有参构造方法)
<bean id="hello" class="com.study1.pojo.Hello">
<constructor-arg type="java.lang.String" value="nihao"/> //如果有参构造有重复类型,则不能使用
</bean>
2.5.4 直接通过参数名(使用有参构造方法)
<bean id="hello" class="com.study1.pojo.Hello">
<constructor-arg name="name" value="nihao2"/> name为有参构造里面的参数名
</bean>
2.5.6 ioc创建对象总结
在配置文件加载的时候,容器中管理的所有对象就被初始化了,即使获取同一个类的两个bean,他们也是一样的,是同一个
三.Spring配置
3.1别名
<alias name="hello" alias="first"/> //name后面的值是bean的id
3.2 bean的配置
<bean id="hello" class="com.study1.pojo.Hello" name="helloNew1,helloNew2">
</bean>
- id:bean的唯一标识符,相当于对象名
- class:对象所对应的全限定名
- name:也是别名,可以取多个别名
3.3 import
这个一般用于团队开发使用,可以将多个配置文件,导入合并为一个
<?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">
导入别的xml文件
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
</beans>
四.依赖注入
4.1 构造器注入
见2.5,有参构造
4.2 set方式注入(重点)
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
4.2.1普通值注入,直接使用name,value
<bean id="student" class="com.study1.pojo.Student">
<property name="name" value="小明"/>
</bean>
4.2.2 bean注入
<bean id="address" class="com.study1.pojo.Address">
<property name="address" value="beijing"/>
</bean>
<property name="address" ref="address"/>
4.2.3 数组注入
<property name="books" >
<array>
<value>语文</value>
<value>数学</value>
</array>
</property>
4.2.4 List注入
<property name="hobbys">
<list>
<value>篮球</value>
<value>足球</value>
</list>
</property>
4.2.5 map注入
<property name="card">
<map>
<entry key="学号" value="123"/>
<entry key="年纪" value="22"/>
</map>
</property>
4.2.6 set注入
<property name="games">
<set>
<value>CF</value>
<value>Lol</value>
</set>
</property>
4.2.7 null注入
<property name="wife">
<null></null>
</property>
空注入:
<property name="wife" value="">
</property>
4.2.8 properties注入
<property name="info">
<props>
<prop key="学号">1111 </prop>
<prop key="age">24 </prop>
</props>
</property>
4.3 P命名空间注入
<!-- p表示的是propertity-->
<bean id="user" class="com.study1.pojo.User" p:name="小明" p:age="12"/>
4.4 C命名空间注入(通过构造器注入)
<!-- 需要有参构造器-->
<bean id="user2" class="com.study1.pojo.User" c:age="12" c:name="jack"/>
五.bean的作用域
5.1 singleton 单例模式(默认的)
同一个类每次getbean都是同一个对象
5.2 prototype 原型模式
<bean id="student" class="com.study1.pojo.Student" scope="prototype">
六.bean的自动装配
6.1 ByName
会自动根据set方法后面的名字在容器中来找对应的id,如果找到,自动赋值
<bean id="address" class="com.study1.pojo.Address">
</bean>
<bean id="student" class="com.study1.pojo.Student" autowire="byName"/>
6.2 ByType
会在容器上下文中查找,和自己对象属性类型相同的id,但是类型必须唯一
<bean id="address" class="com.study1.pojo.Address">
</bean>
<bean id="student" class="com.study1.pojo.Student" autowire="byName"/>
小结:
- ByName的时候,需要保证所有bean的id唯一,并且这个bean的id值需要和自动注入属性set后面的名字一样
- bytype的时候,需要保证所有的class唯一(一个类只能在容器中出现一次),并且这个bean的id值需要和自动注入属性类型一致
6.3 使用注解实现自动装配
<?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.study1.pojo"/>
<bean id="address" class="com.study1.pojo.Address">
</bean>
<bean id="student" class="com.study1.pojo.Student" >
</beans>
@Autowired //在对应属性上方添加注解,可以不需要set方法,需要属性名字和ioc容器中id相同
private Address address;
@Resource //作用与AutoWired相似,但是更高级,如果没有名字相同的,回去找类型相同的
七.注解开发
7.1 使用注解需要导入context约束,增加注解的支持
<?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.study1"/>
<!-- 开启注解支持-->
<context:annotation-config></context:annotation-config>
</beans>
7.2 bean注入和属性注入
@Component //在类上方加注解,注册为bean
public class User {
@Value("jack") //给属性赋值
private String name;
private int age;
7.3 衍生注解
7.3.1 dao层
@Repository
public class UserDao {
}
7.3.2 service层
@Service
public class UserService {
}
7.3.3 controller层
@Controller
public class UserController {
}
这四个注解功能都是一样的,都是将某个类注册到Spring中,装配bean
7.4 作用域
@Component
@Scope("singleton")
public class User {
private String name;
private int age;
}
八.使用java方式配置spring
8.1 配置类
@Configuration //表示是一个配置类
@ComponentScan("com.study1") //扫描哪个包
public class applicationConfig {
// @bean相当于注册一个bean,相当于之前写的一个bean标签
//方法的名字,就是bean标签中的id属性
//方法的返回值,就是class属性,相当于类型
@Bean
public User user(){
return new User(); //返回值,表示要注入到bean中的对象
}
}
8.2 实体类
@Component //说明这个类被spring接管了,注册到容器中
public class User {
@Value("lisa")
private String name;
private int age;
}
8.3 测试
public class HelloTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(applicationConfig.class);
User user = (User) context.getBean("user"); //与方法名要相同
System.out.println(user);
}
}
九.AOP
9.1 、什么是AOP
AOP是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发效率。
9.2使用spring实现Aop
使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
9.2.1、方式一:使用spring的API接口实现AOP
1.service接口和serviceImpl实现类
如果有xml文件,不用使用@component注解
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
2.在applicationContext.xml里注册
<bean id="userServiceImpl" class="com.study2.service.UserServiceImpl"/>
<bean id="log" class="com.study2.log.Log"/>
<bean id="afterlog" class="com.study2.log.AfterLog"/>
<!-- 配置AOP-->
<aop:config>
<!-- 切入点,也就是在哪里配置,execution()里面的表示要在哪里执行-->
<aop:pointcut id="pointcut" expression="execution(* com.study2.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
3.BeforeLog类和AfterLog类
//在执行之前输出该日志
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
//执行之后输出日志
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);
}
}
4、测试
public class HelloTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是一个接口,所以要用UserService,用接口的原因是可以代理多个实现类
//向上转型,还是优先使用子类的实现方法
UserService userService = (UserService) applicationContext.getBean("userServiceImpl"); //输入的是在xml文件里注册的那个,是实现类
userService.query();
}
}
9.2.2、方式二:使用注解实现
1.在容器中注册切面类和开启切面注解支持
<!--注册切面类-->
<bean id="annotationPointCut" class="com.study2.annotation.AnnotationPointCut"/>
<!-- 开启切面注解支持-->
<aop:aspectj-autoproxy/>
2.实现切面类
@Aspect //标记为切面类 只有该类在容器中注册过,才会生效
public class AnnotationPointCut {
@Before("execution(* com.study2.service.UserServiceImpl.*(..))") //execution里面的是执行操作的位置
public void before()
{
System.out.println("方法执行前");
}
@After("execution(* com.study2.service.UserServiceImpl.*(..))")
public void after()
{
System.out.println("方法执行后");
}
}
3.测试
public class HelloTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是一个接口,所以要用UserService,用接口的原因是可以代理多个实现类
//向上转型,还是优先使用子类的实现方法
UserService userService = (UserService) applicationContext.getBean("userServiceImpl"); //输入的是在xml文件里注册的那个,是实现类
userService.query();
}
}
结果:
方法执行前
查询了一个用户
方法执行后
十、整合Mybatis
10.1.导入相关jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependencys/>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
10.2、回顾Mybatis
10.2.1编写实体类
@Data
public class User {
private int id;
private String name;
private int age;
}
10.2.2编写核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 起别名-->
<typeAliases>
<package name="com.study3.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注册一下mapper--> !!!重要
<mappers>
<mapper class="com.study3.mapper.UserMapper"/>
</mappers>
</configuration>
10.2.3编写Mapper接口和Mapper.xml
public interface UserMapper {
public List<User> selectUser();
}
<!--UserMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study3.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
</mapper>
10.2.4、测试
@Test
public void test1() throws IOException {
String resource="Mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for(User u:users)
System.out.println(u);
}
10.3、spring整合Mybatis(方式一:sqlSessionTemplate)
10.3.1 编写spring—dao.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DataSource,使用spring管理数据源-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!-- 绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:Mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/study3/mapper/*.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapperImpl" class="com.study3.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
10.3.2 UserMapperImpl
public class UserMapperImpl implements UserMapper {
//之前所有操作都是使用sqlsession执行,现在都是使用sqlSessionTemplate
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.sqlSessionTemplate=sqlSessionTemplate;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
10.3.4 UserMapper和UserMapper.xml不变
10.3.5测试
@Test
public void test1() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
//getbean前面使用接口,方便复用
UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class);
List<User> users = userMapper.selectUser();
for(User u:users)
System.out.println(u);
}
10.4 spring整合Mybatis
10.4.1、修改xml文件,不需要sqlSessionTemplate
<!-- DataSource,使用spring管理数据源-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!-- 绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:Mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/study3/mapper/*.xml"/>
</bean>
不需要sqlsessiontemplate
<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">-->
<!-- <constructor-arg index="0" ref="sqlSessionFactory"/>-->
<!-- </bean>-->
<!-- <bean id="userMapperImpl" class="com.study3.mapper.UserMapperImpl">-->
<!-- <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>-->
<!-- </bean>-->
<bean id="userMapperImpl2" class="com.study3.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
10.4.2 、修改UserMapperImpl
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
10.4.3 、 别的都不需要改
十一、声明式事务
11.1、什么是事务
- 把一组业务当成一个整体业务来做,要么同时成功,要么同时失败
- 事务在项目开发中十分重要,涉及到数据一致性问题
- 确保完整性和统一性
事务的ACID原则:
- 原子性 :原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
- 一致性:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
- 隔离性:隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离
- 持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
11.2、声明式事务实现
只需要在xml文件中:
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!-- 结合AOP实现事务的织入-->
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/> //所有方法都执行事务
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.study3.mapper.*.*(..))"/> //哪里的文件执行切入操作
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>