一、Spring简介和优点
- Spring是一个开源的免费的框架
- Spring是一个轻量级的非入侵式的框架
- 控制反转Ioc,面向切片编程AOP
- 支持事务处理,对框架的整合的支持
- SpringBoot是一个快速开发的脚手架
- 基于SpringBoot可以快速才发单个微服务
- SpringCloud协调微服务
二、Spring七大组件
- 核心容器(Spring core)
- Spring上下文(Spring context)
- Spring面向切面编程(Spring AOP)
- Spring DAO模块
- Spring ORM模块
- Spring Web模块
- Spring MVC框架(Spring WebMVC)
三、IOC思想
- 程序主动创建对象------------》程序编程被动的接收对象
- 实现IOC的一种方式:DI。实现控制反转的是IOC容器,实现方法是依赖注入
四、第一个Spring程序
hello,applicationContext.xml,test
所谓的IOC,一句话,对象由Spring容器来创建,管理,装配
五、继承关系和IOC执行流程和
六、IOC创建对象的方式(即通过属性赋值,属性注入,赋值完,即创建了一个对象)
-
使用无参构造方法创建(默认)
-
使用有参构造方法创建
- 下标赋值
- 类型赋值
- 参数名赋值(推荐)
<bean> <constructor-arg name="username" value="simon"/> </bean>
七、Spring配置文件
-
别名
- alias标签
- bean中的name属性
-
bean中的scope属性,singleton,prototype
-
import标签:将多个配置文件合为一个
-
property name="" value=""/ref=""
八、依赖注入
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性由容器来注入
- 构造器注入(见六)
- set方法注入(所有参数类型的注入方式,8种)
- p/c命名空间注入
九、作用域Scope=
- singleton/
- prototype
- request
- session
- application
- websocket
十、自动装配(在实际开发中用得很多)
- 一般情况,需要我们手动去Spring容器种注册Bean,即手动装配
- Spring会在上下文中自动寻找Bean,并自动给Bean装配属性
autowired=""
- byName:会自动在容器上下文中找,和自己全限定名路径下的set方法后面的值对应的bean的ID
- byType:会自动在容器上下文中找,和自己对象属性(比如在people类文件中,有以下内容,即为属性),类型相同的bean的ID
public class people{
private Cat cat;
pricate Dog dog;
}
装配种类
-
手动在xml种显式配置
-
Java种显式配置
-
自动隐式装配(掌握)
-
通过属性autowired
-
通过注解(重要)
十一、注解开发
11.1、自动装配的注解实现
- @Nullable 表明这个属性可以为null
- @Autowired Required属性默认true,如果手动的设置为false,说明这个对象可以为null,否则不允许为空
- @Qualifier(value=“dog222”)//显式的将一个属性和Ioc容器中的一个id为dog222的bean绑定
- private Dog dog;
- 用该注解配合@Autowired去使用
- @Primary
- @Resource:Java的注解,没有spring高级
- 先通过名字去查找
- 再通过类名去查找
- @Resource(name=“cat1”)
- private Cat cat;
导入注解支持(开启Ioc自动装配的注解)
<context:annotaion-config/>
<bean id="people" class="com.simon.entity.People"/>
<bean id="dog" class="com.simon.entity.Dog"/>
<bean id="cat" class="com.simon.entity.Cat"/>
@Autowired
-
在属性上用
-
或者在set方法上使用
public class people{
@Autowired
@Qualifier(value="cat333")
private Cat cat;
@Autowired
@Qualifier(value="dog222")
pricate Dog dog;
}
11.2、Spring注解开发(必须保证aop的包导入)
bean
指定要扫描的包
<context:component-scan base-package="com.simon.entity"/>
开启注解支持
<context:annotation-config/>
@Conponent
-
添加在类名上
-
等价于
<bean id="user" class="com.simon.entity.User">
属性的注入
@Value
@Component
public class User{
@Value("Simon")
public String name;
}
-
相当于
<bean id="user" class="com.simon.entity.User"> <property name="name" value="Simon"/> </bean>
衍生的注解
- @Component
- @Repository 与@Component功能一样,都会在Spring容器中注册为一个Bean,一般用在Dao层
- @Servcie 与@Component功能一样,都会在Spring容器中注册为一个Bean,一般用在Servcie层
- @Controller 与@Component功能一样,都会在Spring容器中注册为一个Bean,一般用在Controller层
自动装配(见上面)
作用域
@Scope
@Scope("singleton")
public class User{
}
小结
-
xml更加万能,适用于任何场合
-
注解—不是自己的类用不了,维护相对复杂,需要一个个文件的点进去查看
-
最佳实现
- xml用来管理bean
- 注解只用来属性注入
使用注解,需要开启注解支持
指定要扫描的包
<context:component-scan base-package="com.simon.entity"/>
开启注解支持
<context:annotation-config/>
十二、使用Java的方式配置spring,(新特性)
-
类似于xml配置文件的功能
-
JavaConfig以前是Spring的一个子项目
-
这种纯Java的配置方式,在SpringBoot中随处可见
-
SpringBoot是一个脚手架,无法修改
@Configuration代表这是一个配置类,类似于xml配置文件
@Bean相当于xml配置文件中的bean标签、
-
方法名就相当于bean标签中的id属性、
-
方法返回值就相当于标签中的class属性
-
方法返回值,就是返回一个对象
如果完全使用了这个JavaConfig配置类实现开发,需要使用AnnotaionConfigApplicationContext去替代ClasPathXmlApplicationContext上下文
@Configuration
@ComponentScan("包名")
@Import(AppConfig2.class)
public class AppConfig{
@Bean
public MyService myservice(){
return new MyServiceImpl();
}
}
@Component
public class MyServiceImpl{
}
等价于
<bean id="myService" class="com.simon.service.MyService"/>
👆
Ioc Spring容器中的类,拿出来,就是一个对象
AOP
👇
十三、代理模式
静态代理
UserService.java
package com.simon.demo1;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
UserServiceImpl.java
package com.simon.demo1;
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 select() {
System.out.println("查询一个用户");
}
}
UserServiceProxy.java
package com.simon.demo1;
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void select() {
log("select");
userService.select();
}
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}
Client.java
package com.simon.demo1;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
动态代理
Proxy类和InvocationHandler类
Rent.java
package com.simon.demo2;
public interface Rent {
public void rent();
}
Host.java
package com.simon.demo2;
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
ProxyInvocationHandler.java
package com.simon.demo2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成的到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
@Override
//处理代理实例,并且返回处理结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的实质,就是通过反射机制实现
Object result = method.invoke(rent, args);
fee();
return result;
}
public void seeHouse(){
System.out.println("中介带你看房子");
}
public void fee(){
System.out.println("收中介费fee");
}
}
Client.java
package com.simon.demo2;
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色 来处理我们将要调用的接口对象
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy();//这里的Proxy就是动态生成的代理类
proxy.rent();
}
}
ProxyInvocationHandler.java
package com.simon.demo03;
import com.simon.demo2.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成的到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override
//处理代理实例,并且返回处理结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的实质,就是通过反射机制实现
Object result = method.invoke(target, args);
return result;
}
}
Client.java
package com.simon.demo03;
import com.simon.demo1.UserService;
import com.simon.demo1.UserServiceImpl;
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService)pih.getProxy();
proxy.add();
}
}
十四、AOP的三种实现方式
UserService.java
package com.simon.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
UserServiceImpl.java
package com.simon.service;
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 select() {
System.out.println("查找了一个用户");
}
}
applicationContext.xml
<bean id="userService" class="com.simon.service.UserServiceImpl"/>
<bean id="log" class="com.simon.log.Log"/>
<bean id="afterLog" class="com.simon.log.AfterLog"/>
方法一:
-
使用原生Spring API接口
-
配置aop:需要导入aop的约束
AfterLog.java
package com.simon.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
Log.java
package com.simon.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"执行了");
}
}
<aop:config>
<!--切入点,expression: 表达式,execution(要执行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.simon.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
方法二—自定义类,DIY类
DiyPintCut.java
package com.simon.diy;
public class DiyPointCut {
public void before(){
System.out.println("============方法执行前==========");
}
public void after(){
System.out.println("============方法执行hou==========");
}
}
applicationContext.xml
<bean id="diy" class="com.simon.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,即切入点-->
<aop:aspect ref="diy">
<!--自定义切面-->
<aop:pointcut id="point" expression="execution(* com.simon.service.UserServiceImpl.*(..))"/>
<!--通知方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方法三—通过注解的方法
AnnotationPointCut.java
package com.simon.diy;
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.simon.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("============方法执行前==========");
}
@After("execution(* com.simon.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("============方法执行hou==========");
}
@Around("execution(* com.simon.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("====环绕前==========");
System.out.println(jp.getSignature());//获得签名
//执行方法
Object proceed = jp.proceed();
System.out.println("后");
}
}
applicationContext.xml
<bean id="annotationPointCut" class="com.simon.diy.AnnotationPointCut"/>
<!--也可以不在这里用标签注册,
在类名上添加一个@Component即可完成注册
-->
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
<!--DateSource:Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC: org.springframework.jdbc.datesource
-->
<bean id="dateSource" class="org.springframework.jdbc.datesource.DriverManagerDateSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="driver" value="jdbc:mysql:http://localhost:3306/testdb:"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dateSource" ref="dateSource"/>
<!--绑定mybatis配置文件-->
<property name="configuration" value="classpath:mybatis-config.xml"/>
</bean>
<!--SqlSessionFactoryTemplate就是我们的SqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能通过构造器注入sqlSessionFactory 因为底层源码没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
十四、Mybatis-Spring的整合
创建mapper和sqlSession并且注入到bean中
mybatis3.5+
spring5+
还有一个mybatis-spring2.0+
方法一、SqlSessionTemplate
要使mybatis和spring一起使用,最少需要导入两个东西
,一个sqlSessionFactory和是少一个数据映射类
在mybatis-Spring中,可以使用SqlSessionFactoryBean来创建SqlSessionFactory。而要配置这个bean只需要把以下代码放入Spring的xml文件中
注意:SqlSessionFactory
需要一个 DataSource
(数据源)
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--dataSource 使用spring-jdbc替换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/mybatis?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--就是我们使用的SqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能用构造器注入,sqlSessionFactory,没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(String userId) {
return sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
}
}
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
</bean>
写一个UserDao实现类UserDaoImpl,把sqlSession私有化,然后在Spring容器中注册UserDaoImpl,并且把sqlSessionTemplate注入到实现类中去
方法二、SqlSessionDaoSupport
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
public User getUser(String userId) {
return getSqlSession().selectOne("com.simon.dao.UserMapper.getUser", userId);
}
}
调用 getSqlSession()
方法你会得到一个 SqlSessionTemplate
,之后可以用于执行 SQL 方法
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
需要注入一个sqlSessionFactory,因为父类需要注入一个会话工厂
如果这样是实现了,就不需要在Spring容器中注册SqlSessionTemplate了
十五、事务(待完善)
事务Transaction
事务的ACID原则
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源
- 持久性
- 事务一旦提交,无论系统发生什么
原本应该是spring的事务。
MyBatis-Spring 允许 MyBatis 参与到 Spring 的事务管理中。
而不是给 MyBatis 创建一个新的专用事务管理器,
MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager
来实现事务管理。
一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。
并且支持 @Transactional
注解和 AOP 风格的配置。
在事务处理期间,一个单独的 SqlSession
对象将会被创建和使用。
当事务完成时,这个 session 会以合适的方式提交或回滚。
事务配置好了以后,MyBatis-Spring 将会透明地管理事务。
这样在你的 DAO 类中就不需要额外的代码了。
标准配置
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager
对象:
需要一个数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tc:attributes>
<tx:method name="*" propaganda="REQUIRED"/>
</tc:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.simon.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
声明式事务:在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。
编程式事务:需要在代码中进行事务的管理
public class UserService {
private final PlatformTransactionManager transactionManager;
public UserService(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void createUser() {
TransactionStatus txStatus =
transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
userMapper.insertUser(user);
} catch (Exception e) {
transactionManager.rollback(txStatus);
throw e;
}
transactionManager.commit(txStatus);
}
}
声明式事务控制明确事项:
核心业务代码(目标对象) (切入点是谁?)
事务增强代码(Spring已提供事务管理器))(通知是谁?)
切面配置(切面如何配置?)
十六、总结
拓展(ApplicationContext对象的来源)
- ApplicationContext
- 👆继承
- ConfigurableApplicationContext
- 👆实现接口
- AbstractApplicationContext
- 👆继承
- AbstractRefreshableApplicationContext
- 👆继承
- AbstractRefreshableConfigApplicationContext
- 👆继承
- AbstractXmlApplicationContext
- 👆继承继承
- ClassPathXmlApplicationContext