Spring
1. Spring
组成--七大模块
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.11.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.11.RELEASE</version> </dependency>
扩展
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于SpringBoot实现的
2. IOC理论
控制反转
程序员不用再管理对象的创建了,系统的耦合性大大降低。可以更加专注在业务的实现。
IOC : 控制反转
DI : 依赖注入,控制反转的一种实现方式
- 反转:程序本身不创建对象,编程被动的接收对象
- 依赖注入 : 利用set方法进行注入
- 对象是由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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="Hello" class="com.Rickduck.pojo.Hello"> <property name="str" value="Spring" /> </bean> </beans>
IOC创建对象的方式
无参构造方式创建
<bean id="user" class="com.Rickduck.pojo.User" name="u1,u2"> </bean>
有参构造方式
下标赋值
<!-- 第一种,下标赋值--> <bean id="user" class="com.Rickduck.pojo.User"> <constructor-arg index="0" value="index-ref" /> </bean>
通过类型创建
<!-- 第二种,通过类型创建,不建议使用--> <bean id="user" class="com.Rickduck.pojo.User"> <constructor-arg type="java.lang.String" value="type-ref" /> </bean>
直接通过参数名
<bean id="user" class="com.Rickduck.pojo.User"> <property name="user" value="Spring"> </bean>
在加载配置文件的时候,容器中管理的对象就已经初始化了!!!
3. Spring配置
3.1 别名
<!-- 配置别名 --> <alias name="name" alias="newName"></alias>
3.2 Bean的配置
<!--id:唯一标识 class:bean对象对应的全限定名 name:别名(可以取多个别名)--> <bean id="user" class="com.Rickduck.pojo.User" name="u1,u2"> </bean>
3.3 import
一般用于团队开发,可以将多个配置文件导入合并为一个。
<import resource="beans.xml" />
4. 依赖注入
4.1 构造器注入
4.2 Set方式注入
- 依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象钟的所有属性都由容器来注入!
8种类型注入:
<bean id="student" class="com.Rickduck.pojo.Student"> <!-- 1.普通值注入--> <property name="name" value="Rickduck" /> <!-- 2.Bean注入--> <property name="address" ref="address" /> <!-- 3.数组注入--> <property name="books"> <array> <value>水浒传</value> <value>红楼梦</value> <value>三国演义</value> <value>西游记</value> </array> </property> <!-- 4.List注入--> <property name="hobbys"> <list> <value>歌曲</value> <value>代码</value> <value>篮球</value> </list> </property> <!-- 5.Map注入--> <property name="card"> <map> <entry key="ID" value="123456" /> <entry key="key" value="value" /> </map> </property> <!-- 6.Set注入--> <property name="games"> <set> <value>LOL</value> <value>QQ飞车</value> </set> </property> <!-- 7.空值注入--> <property name="wife"> <null /> </property> <!-- 8.Properties注入--> <property name="info"> <props> <prop key="学号">123456</prop> <prop key="姓名">2333</prop> </props> </property> </bean>
4.3 扩展方式注入
p命名空间方式注入
c命名空间方式注入
注意点 :p命名、c命名空间不能直接使用,需要导入xml约束
4.4 Bean的作用域
单例模式(默认)
- 每次从容器中get取得时候都是同一个对象,只有一个实例对象
原型模式
- 每次从容器中get的时候都会产生一个对象
其余得request、session、application 这些只在web中使用
5. Bean的自动装配
- 自动装配是Spring满足bean依赖得一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性
三种Spring装配方式
- xml中显示配置
- java中显示配置
- 隐式的自动装配bean(重要)
<bean id="cat" class="com.Rickduck.pojo.Cat"></bean> <bean id="dog" class="com.Rickduck.pojo.Dog"></bean> <!-- byName:自动在容器上下文和自己对象set方法值对应的beanid byType:自动在容器上下文和自己对象属性类型相同的bean(保证全局唯一) --> <bean id="person" class="com.Rickduck.pojo.Person" autowire="byName"> <property name="name" value="Rickduck"></property> </bean>
- byName:保证beanid唯一,并且和需要注入属性set方法的值一致
- byType:保证bean的class唯一
使用注解实现自动装配
jdk1.5支持注解
spring2.5支持注解
步骤:
- 导入约束:context约束
- 配置注解的支持:context:annotation-config/
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解支持 --> <context:annotation-config/> </beans>
@Autowired
- 直接在属性上使用
- 可不使用set方法
- 默认byType
@Qualifier
- 可指定属性
- value = "name"
@Resouce
- 默认byName
6.使用注解开发
1.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解支持 --> <context:annotation-config/> <!-- 开启包扫描 --> <context:component-scan base-package="com.Rickduck"/> </beans>
2.属性注入
- @Component
- 组件,用在pojo类上,
@Component public class User{ public String name; //相当于applicationContext.xml中配置Bean时的属性注入 @value("Rickduck") public void setName(String name){ this.name = name; } }
3.衍生的注解
@Component有几个衍生注解,在web开发中会按照mvc三层架构分层!
- dao : @Repository
- service : @Service
- controller : @Controller
4. 自动装配
- @Autowired : 自动装配通过类型。名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx") - @Nullable : 说明该字段可以为null - @Resource : 自动装配通过名字。类型
5.作用域
@Component @Scope("prototype") public class User{ public String name; //相当于applicationContext.xml中配置Bean时的属性注入 @value("Rickduck") public void setName(String name){ this.name = name; } }
6. 小结
xml与注解
- xml更加万能,适用于任何场合!
- 注解不是自己的类不能用,维护相对复杂!
7.使用Java方式配置Spring
JavaConfig是Spring一个子项目,在Spring4之后成为一个核心功能
实体类
@Component public class User{ private String name; public String getName(){ return name; } @value("Rickduck") //属性值注入 public void setName(String name){ this.name = name; } }
配置类:
@Configuration @ComponentScan("com.Rickduck.pojo") @Import(Rickduck2.class) public class RickduckConfig{ // 注册一个bean,相当于xml中的bean标签 // 方法名 : bean的id // 方法返回值 :bean的class属性 @Bean public User getUser(){ return new User(); } }
测试类
public class MyTest{ public static void main(String[] args){ //使用了配置类方式,只能通过AnnotationConfig上下文来获取容器 ApplicationContext context = new AnnotationConfigApplicationContext(RickduckConfig.class); User getUser = (User)context.getBean("getBean"); System.out.println(getUser.getName()); } }
纯Java的配置方式,在SpringBoot中随处可见
8. 代理模式
SpringAOP的底层是代理模式
代理模式的分类:
- 静态代理
- 动态代理
8.1 静态代理
角色分析
- 抽象角色 :一般会使用接口或者抽象类来解决
- 真实角色 : 被代理的角色
- 代理角色 :代理真实角色(代理真实角色后一般会做一些附属操作)
- 客户:访问代理对象的人
步骤:
接口
//租房 public interface Rent{ public void rent(); }
真实角色
//房东 public class Host implements Rent{ public void rent(){ System.out.println("房东租房!"); } }
代理角色
public class Proxy implements Rent{ private Host host; public Proxy(){} public Proxy(Host host){ this.host = host; } public void rent(){ seeHouse(); host.rent(); contract(); fare(); } //看房 public void seeHouse(){ System.out.println("中介带看房!"); } //合同 public void contract(){ System.out.println("签租赁合同!"); } //收取中介费 public void fare(){ System.out.println("收中介费!"); } }
客户端访问代理角色
public class Client{ public static void main(String[] args){ //房东要租房子 Host host = new Host(); //代理,中介帮房东出租房子;中间会加上一些附属操作 Proxy proxy = new Proxy(host); //客户不需要面对房东,著需要面对中介 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更纯粹,不用去关注一些公共的业务
- 公共也就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,开发效率变低
8.2 动态代理
底层是反射
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的:基于接口、基于类的动态代理
- 动态代理分为两大类
- 基于接口 -- JDK动态代理
- 基于类 -- cglib
- java字节码实现:javasist
两个类 : Proxy代理、InvocationHandler:调用处理程序
Proxy : 提供创建动态代理类和实例的静态方法
InvocationHandler:调用处理程序并返回结果
InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler{ //被代理的接口 private Object target; public void setTarget(Object target){ this.target = target; } //生成得到代理类 //类加载器,代理接口,处理程序 public Object getProxy(){ return Proxy.newProxtInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces,this); } //处理代理实例,并返回结果 public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ log(method.getName()); Object result = method.invoke(target,args); return result; } public void log(String msg){ System.out.println("执行了"+msg+"方法"); } }
测试类
public class Clent{ public static void main(String[] args){ //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理对象通过处理程序动态生成 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设置需要代理的对象 pih.setTarget(userService); UserService proxy = (UserService)pih.getProxy(); proxy.query(); } }
9. AOP
SpringAOP中,通过Adivice定义横切逻辑,Spring中支持五种类型的Advice:
9.1 Spring实现AOP
依赖导入:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> <scope>runtime</scope> </dependency>
方式一:Spring的API接口
AOP配置:
需要增加aop的约束!
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注册bean --> <bean id="userService" class="com.Rickduck.service.UserServiceImpl" /> <bean id="log" class="com.Rickduck.log.Log" /> <bean id="afterlog" class="com.Rickduck.log.AfterLog" /> <!-- 配置aop:需要导入aop的约束 --> <aop:config> <!-- 切入点: expression:表达式,execution(执行的位置! * * *)--> <aop:pointcut id="pointcut" expression="execution(* com.Rickduck.service.UserServiceImpl.*(..))"/> <!-- 执行环绕增加 --> <aop:advisor advice-ref="log" pointcut-ref="pointcut" /> <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut" /> </aop:config> </beans>
前置通知
public class Log implements MethodBeforeAdvice { /* * method : 要执行的目标对象的方法 * args : 参数 * target : 目标对象 */ @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{ //returnValue : 返回值 @Override public void afterReturning(Object returnValue,Method method,Object[] args,Object target) throws Throwable{ System.out.println("执行了"+target.getClass().getName()+"方法,返回结果为:"+returnValue); } }
测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserService userService = context.getBean("userService",UserService.class); userService.add(); } }
方式二 自定义切入点类
自定义切面类
public class DiyPointCut { public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }
aop配置
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注册bean --> <bean id="userService" class="com.Rickduck.service.UserServiceImpl" /> <bean id="diy" class="com.Rickduck.diy.DiyPointCut" /> <aop:config> <!-- 自定义切面 --> <aop:aspect ref="diy"> <!-- 切入点 --> <aop:pointcut id="point" expression="execution(* com.Rickduck.service.UserServiceImpl.*(..))"/> <aop:before method="before" pointcut-ref="point" /> <aop:after method="after" pointcut-ref="point" /> </aop:aspect> </aop:config> </beans>
方式三:注解实现AOP
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class AnnotationPointCut { @Before("execution(* com.Rickduck.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前"); } @After("execution(* com.Rickduck.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后"); } //在环绕增强中 @Around("execution(* com.Rickduck.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); //执行方法 Object proceed = jp.proceed(); System.out.println("环绕后"); } }
<!-- 注册切面类bean --> <bean id="annotationPointCut" class="com.Rickduck.diy.AnnotationPointCut" /> <!-- 开启注解支持 默认JDK实现--> <aop:aspectj-autoproxy />
10. 整合Mybatis
10.1 Mybatis搭建:
步骤:
导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring
编写配置文件
测试
10.2 Mybatis-spring
编写数据源配置
sqlSessionFactory
sqlSessionTemplate
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- DataSource : 使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid--> <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/mybatics?useSSL=false&useUnicode=true&characterEncoding=UTF-8" /> <property name="username" value="root"/> <property name="password" value="888888"/> </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/Rickduck/mapper/*.xml" /> </bean> <!-- SqlSessionTemplate:就是我们使用的sqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory,因为没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> <bean id="userMapper" class="com.Rickduck.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession" /> </bean> </beans>
需要给接口加实现类
测试
public class MyTest { //mybatis @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sessionFactory.openSession(true); UserMapper user = sqlSession.getMapper(UserMapper.class); List<User> userList = user.selectUser(); for (User user1 : userList) { System.out.println(user1); } } //Spring整合mybatis后 @Test public void test2() throws IOException{ ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper userMapper = context.getBean("userMapper",UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println(user); } } }
实现二:
通过实现类继承SqlSessionDaoSupport
import com.Rickduck.pojo.User; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser() { return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
11. 声明式事务
11.1 事务
- 把一组业务当成一个业务做:要么都成功要么都失败
- 设计数据一致性
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 要么都成功要么都失败
- 一致性
- 事务执行前后数据库处于一致的状态
- 隔离性
- 并发事务是相互隔离的,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题结构都不在被影响,被持久化到数据库中
11.2 spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中对事务进行管理
事务的传播级别:
在applicationContext中进行事务的配置
<!-- 配置声明式事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 结合AOP实现事务的织入 --> <!-- 配置事务通知: --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给哪些方法配置事务 --> <!-- 配置事物的传播特性: new propagation--> <tx:attributes> <tx:method name="add" /> <tx:method name="delete" /> <tx:method name="update" /> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 配置事务切入 --> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.Rickduck.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" /> </aop:config>
为什么需要事务:
- 如果不配置事务,可能存在数据提交不一致的情况
- 如果不在Spring中配置声明式事务,就需要在代码中手动配置事务
- 事务十分重要!!!