Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器
Spring组成
Spring框架是一个分层架构,由七个模块组成。构建在核心容器上(Spring Core),核心容器定义了创建,配置和管理Bean的方式。
每个模块的功能如下:
- 核心容器(Spring Core):主要组件是BeanFactory。定义了创建,配置和管理Bean的方式
- Spring上下文(Spring Context):Spring Context是一个配置文件,向Spring框架提供上下文信息
- Spring AOP:通过配置管理特性,Spring AOP将面向切面编程的功能集成到Spring框架中
- Spring DAO:面向JDBC的数据库访问接口
- Spring ORM:插入多个框架来提供ORM对象关系映射工具,包括JDO,Hibernate,iBatis
- Spring WEB:为Web应用提供上下文
- Spring MVC:提供构建Web应用的MVC实现
IOC本质
控制反转(Inversion Of Control)IOC是一种设计思想,依赖注入(Dependency Injection)DI是一种方法
程序创建对象==>容器创建对象
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建并组织对象存入IOC容器中,当程序需要使用对象时从容器中取出对象即可
Spring程序
导入jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.5</version> </dependency>
编写实体类
public class Hello { private String name; public Hello(){ System.out.println("调用无参构造"); } public String getName(){ return name; } public void setName(String name){ this.name=name; } public void show(){ System.out.println("Hello,"+name); } }
编写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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="hello" class="com.deng.Entity.Hello"> <property name="name" value="Spring"></property> </bean> </beans>
测试
public void test1(){ //解析beans.xml配置文件,生成管理bean的对象 ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); //根据bean的id获取bean对象 Hello hello=(Hello)context.getBean("hello"); hello.show(); }
依赖注入
- 依赖:Bean对象的创建依赖于容器
- 注入:Bean对象所依赖的资源由容器来设置和装配
构造器注入
<bean id="hello" class="com.deng.Entity.Hello">
<!-- name指参数名 -->
<constructor-arg name="name" value="Spring"/>
</bean>
常量注入
要求被注入的属性必须由set方法才能注入
<bean id="hello" class="com.deng.Entity.Hello"> <property name="name" value="Spring"></property> </bean>
Bean注入
<bean id="address" class="com.deng.Entity.Address"> <property name="address" value="zhejianghangzhou"></property> </bean> <bean id="hello" class="com.deng.Entity.Hello"> <property name="name" value="Spring"></property> <property name="address" ref="address"></property> </bean>
数组注入
<property name="books"> <array> <value>西游记</value> <value>红楼梦</value> <value>水浒传</value> </array> </property>
List注入
<property name="hobbys"> <list> <value>西游记</value> <value>红楼梦</value> <value>水浒传</value> </list>
</property>
Map注入
<property name="card"> <map> <entry key="中国邮政" value="456456456465456"/> <entry key="建设" value="1456682255511"/> </map> </property>
Set注入
<property name="games"> <set> <value>LOL</value> <value>BOB</value> <value>COC</value> </set> </property>
null注入
<property name="wife"><null/></property>
properties注入
<property name="info"> <props> <prop key="学号">20190604</prop> <prop key="性别">男</prop> <prop key="姓名">小明</prop> </props> </property>
命名空间注入
p命名空间注入
需要在头文件导入约束文件
<beans xmlns:p="http://www.springframework.org/schema/p"> <bean id="hello" class="com.deng.Entity.Hello" p:name="dwx"></bean> </beans>
c命名空间注入
需要在头文件导入约束文件,且必须要有有参构造函数才能使用
<beans xmlns:c="http://www.springframework.org/schema/c"> <bean id="hello" class="com.deng.Entity.Hello" c:name="dwx"></bean> </beans>
Bean的作用域
Bean就是IOC容器初始化,装配和管理的对象,有4种作用域:
singleton
bean的作用域为singleton时,IOC容器中只会存在一个共享的Bean实例,对于所有的bean请求,id与该bean相同就会返回同一实例。
在创建容器时就自动创建了一个bean对象,无论是否使用都存在。
singleton是bean的默认作用域,xml中定义如下
<bean id="hello" class="com.deng.Entity.Hello" scope="singleton"></bean>
prototype
prototype作用域的bean会导致每次对该bean对象请求(将该bean注入到另一个bean中,或者调用getBean()方法)时都会创建一个新的实例。在创建容器时,bean并没有实例化,而是在需要获取bean对象时才会去创建一个对象,并且每次获取的对象都不是同一对象。在xml中的定义如下
request
当bean的作用域为request时,表示每一个http请求都会有各自的bean实例,仅在基于web的SpringApplicationContext里有效。当处理请求结束后,request的作用域的bean实例将被销毁,在xml的定义如下
<bean id="hello" class="com.deng.Entity.Hello" scope="request"></bean>
session
bean的作用域为session,表示一个Http Session对应一个bean实例,仅在基于web的SpringApplicationContext里有效。Http Session结束后,bean也会被销毁
Bean的自动装配
Spring会在应用上下文中为某个bean寻找其依赖的bean
bean的三种装配机制:
- 在xml中显式的配置
- 在java中显式的配置
- 自动装配bean
实现自动装配的两个操作:
- 组件扫描(component scanning):Spring会自动发现应用上下文中创建的bean
- 自动装配(autowiring):Spring自动满足bean之间的依赖,依赖注入
在xml中配置注入
手动配置xml
3个实体类
//猫 public class Cat { public void shout(){ System.out.println("miaomiaomiao!!!"); } } //狗 public class Dog { public void shout(){ System.out.println("wangwangwang!!!"); } } //人 @Data public class User { private Cat cat; private Dog dog; private String name; }
编写配置文件
<?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 id="cat" class="com.deng.Entity.Cat"></bean> <bean id="dog" class="com.deng.Entity.Dog"></bean> <bean id="user" class="com.deng.Entity.User"> <property name="cat" ref="cat"></property> <property name="dog" ref="dog"></property> <property name="name" value="dwx"></property> </bean> </beans>
按名称自动配置xml
修改bean的参数配置,增加属性autowired="byName"
<bean id="cat" class="com.deng.Entity.Cat"></bean> <bean id="dog" class="com.deng.Entity.Dog"></bean> <bean id="user" class="com.deng.Entity.User" autowire="byName"> <property name="name" value="dwx"></property> </bean>
使用autowired byName属性时步骤:
- 查找该类中的所有set方法,去掉set,获取set去掉并且首字母小写的字符串,作为匹配对象
- 去spring容器查找有没有id与此字符串匹配的bean对象
- 如果有就注入,没有就抛出空指针异常
按类型自动配置xml
使用autowire byType要确保同一类型的对象在Spring容器中唯一,如果不唯一就会抛出异常
<bean id="cat985" class="com.deng.Entity.Cat"></bean> <bean id="dog211" class="com.deng.Entity.Dog"></bean> <bean id="user" class="com.deng.Entity.User" autowire="byType"> <property name="name" value="dwx"></property> </bean>
使用注解注入
首先要在xml文件中引入context头文件
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
然后开启属性注解的支持
<context:annotation-config></context:annotation-config>
@Autowired
- Autowired默认是按类型(byType)自动装配的
- 使用它需要导入spring-aop包
public class User { @Autowired private Cat Cat; @Autowired private Dog dog; private String name; }
不需要set方法也可
@Autowired(required=false)//表示允许对象为null
@Qualifier
- 与@Autowired一起使用,表示按照名称(byName)的方式进行自动装配
- 不能单独使用
public class User { @Autowired @Qualifier(value = "cat") private Cat Cat; @Autowired private Dog dog; private String name; }
@Resource
- 如果指定name属性,按照该属性进行查找装配
- 如果没有指定name属性,按照默认的byName方式装配
- 如果byName装配不成功就按byType的方式装配
- 如果都不成功就抛出异常
@Data public class User { @Resource(name = "cat") private Cat cat; @Autowired private Dog dog; private String name; }
@AutoWired与@Resource区别
- @Autowired默认按照类型装配,由Spring提供,如果想要按名称装配,可以结合@Qulifier使用
- @Resource默认按照名称装配,由J2EE提供,名称可以通过name指定。如没有指定,按字段属性名来装配,当找不到与名称匹配的bean时就按照类型进行匹配。一旦指定了name就只会按照name来进行匹配和装配。
- 两者都可以写在字段或set方法上。写在字段上,就按照字段名来装配查找。如果写在set方法上,就按属性名(去掉set,并将首字母小写)来装配查找(不区分首字母大小写)。如果两者都写在字段上,就不需要set方法
使用注解开发
导入spring-aop注解,并在配置文件中引入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.deng.Entity"></context:component-scan> </beans>
在该包下的类增加注解@Component
@Component("user") public class User { public String name="dwx"; }
测试
public void test1() { ApplicationContext context=new ClassPathXmlApplicationContext("beans1.xml"); User user=(User) context.getBean("user"); System.out.println(user.name); }
属性注入
- 直接在字符名添加@Value("值")
@Component("user")
// 相当于配置文件中,<bean id="user" class="com.deng.Entity.User"/>
public class User {
@Value("dwx")
//相当于配置文件中<property name="name" value="dwx">
public String name;
}
- 在set方法上添加@Value("值")
@Component public class User { public String name; @Value("dwx") public void setName(String name) { this.name = name; } }
衍生注解
- @Controller:web层
- @Service:service层
- @Repository:dao层
这些注解都是表示将对应的类交给spring管理
@Scope()作用域
- siingleton:默认的,单例模型,关闭工厂所有对象都会销毁
- prototype:多例模型,关闭工厂,对象不会销毁,而是等待垃圾回收器回收
@Component @Scope("prototype") public class User { public String name; @Value("dwx") public void setName(String name) { this.name = name; } }
基于Java类进行配置
使用
实体类
@Data public class Cat { private String name; }
编写Config配置类
@Configuration//表示这是一个配置类 public class MyConfig { @Bean//通过该方法注册一个bean,bean类型就是返回值,id就是方法名 public Cat cat(){ Cat cat=new Cat(); //注入值 cat.setName("jiafeimao"); return cat; } }
测试
public void test1() { //根据配置类创建applicationContext来管理bean对象 ApplicationContext context=new AnnotationConfigApplicationContext(MyConfig.class); //获取bean对象 Cat cat=(Cat)context.getBean("cat"); System.out.println(cat.getName()); }
导入其他配置类使用
@Import(MyConfig2.class)
代理模式
代理就是在不改变原来的代码的情况下,对原有的功能进行增强
静态代理:
代理一个角色
//抽象角色:租房 public interface Rent { public void rent(); }
//中介 public class StaticProxy { private Host host; public StaticProxy(Host host){ this.host=host; } //租房 public void rent(){ seeHouse(); host.rent(); fare(); } //看房 public void seeHouse(){ System.out.println("看看房子"); } //收中介费 public void fare(){ System.out.println("收取中介费"); } }
//客户端 public class Client { public static void main(String[] args) { Host host=new Host(); StaticProxy staticProxy=new StaticProxy(host); staticProxy.rent(); } }
动态代理:
代理一类角色,动态代理的代理类是动态生成的,静态代理的代理类是提前写好的,需要了解接口InvocationHandler和类Proxy
InvocationHandler里的invoke()方法:调用处理程序
Proxy里的静态方法newProxyInstance()方法:创建代理实例
public class DtProxy 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); } //处理代理实例上的方法并将结果返回 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); Object result=method.invoke(rent,args); fare(); return result; } //看房 public void seeHouse(){ System.out.println("带房客看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
//客户端 public class Client { public static void main(String[] args) { Host host=new Host(); DtProxy dtProxy=new DtProxy(); dtProxy.setRent(host); Rent proxy=(Rent)dtProxy.getProxy(); proxy.rent(); } }
AOP
使用AOP织入要导入依赖包
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
AOP术语通俗解释
- 通知(Adivce):想加入的功能,如日志,安全等
- 连接点(JoinPoint):spring允许添加功能的地方
- 切入点(PointCut):要添加功能的方法
- 切面(Aspect):通知和切入点的结合,干什么,什么时候干,在哪儿干
- 目标(Target):被通知的对象
- 代理(Proxy):给目标对象应用通知后创建的对象
- 织入(Weaving):将切面和目标对象连接起来,并创建代理对象的过程
通过xml配置的方式实现AOP,
通过Spring的API来实现
public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加用户"); } public void delete() { System.out.println("删除用户"); } public void update() { System.out.println("更新用户"); } public void search() { System.out.println("查询用户"); } }
写增强类实现Spring的API
public class Log implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass().getName() + "的" + method.getName() + "方法被执行了"); } }
public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了" + o1.getClass().getName() +"的"+method.getName()+"方法," +"返回值:"+o); } }
最后在spring的配置文件中注册并实现aop切入
<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"> <bean id="userService" class="com.deng.Service.UserServiceImpl"></bean> <bean id="log" class="com.deng.Entity.Log"></bean> <bean id="afterLog" class="com.deng.Entity.AfterLog"></bean> <aop:config> <!--切入点 expression:表达式匹配要执行的方法--> <aop:pointcut id="pointcut" expression="execution(* com.deng.Service.UserServiceImpl.*(..))"/> <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
自定义方法来实现aop
自定义切入类
public class DiyPointCut { public void before(){ System.out.println("---------方法执行前---------"); } public void after(){ System.out.println("---------方法执行后---------"); } }
在Spring中配置
<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"> <bean id="userService" class="com.deng.Service.UserServiceImpl"></bean> <bean id="log" class="com.deng.Entity.Log"></bean> <bean id="afterLog" class="com.deng.Entity.AfterLog"></bean> <bean id="diy" class="com.deng.Entity.DiyPointCut"></bean> <aop:config> <aop:aspect ref="diy"> <aop:pointcut id="pointcut" expression="execution(* com.deng.Service.UserServiceImpl.*(..))"></aop:pointcut> <aop:before pointcut-ref="pointcut" method="before"></aop:before> <aop:after pointcut-ref="pointcut" method="after"></aop:after> </aop:aspect> </aop:config> </beans>
注解方式实现
@Aspect public class DiyPointCut { @Before("execution(* com.deng.Service.UserServiceImpl.*(..))") public void before(){ System.out.println("---------方法执行前---------"); } @After("execution(* com.deng.Service.UserServiceImpl.*(..))") public void after(){ System.out.println("---------方法执行后---------"); } }
xml配置
<bean id="userService" class="com.deng.Service.UserServiceImpl"></bean> <bean id="diy" class="com.deng.Entity.DiyPointCut"></bean> <aop:aspectj-autoproxy/>
Mybatis整合
mybatis-spring
导入依赖
<dependencies>
<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.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
在Mybatis中是通过SqlSessionFactoryBuilder来创建SqlSessionFactory的,而在MyBatis-Spring中是使用SqlSessionFactoryBean来创建的;
然后使用SqlSessionFactory来获取SqlSession,用他来执行映射语句。SqlSessionFactory有一个唯一的必要属性:用于JDBC的数据源DataSource。
SqlSessionTemplate是Myabtis-Spring的核心,是SqlSession的一个实现
可以使用SqlSessionFactory作为构造方法的参数来创建SqlSessionTemplate对象
整合实现
方式一:通过SqlSessionTemplate实现
beans.xml配置文件
sqlSessionFactory的属性configLocation和mapperLocation指定了mybatis和mapper的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 id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="13476110270dwx"></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!--关联Mybatis--> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="mapperLocations" value="classpath:com/deng/dao/*.xml"></property> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg> </bean> <bean id="userDao" class="com.deng.dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"></property> </bean> </beans>
增加接口实现类
public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; public SqlSessionTemplate getSqlSession() { return sqlSession; } public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public User getUser(int id) { UserMapper userMapper=sqlSession.getMapper(UserMapper.class); return userMapper.getUser(id); } }
测试
public void test1(){ ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); UserMapper userMapper=(UserMapper)context.getBean("userDao"); User user=userMapper.getUser(1); System.out.println(user); }
mybatis-config.xml中的配置文件并没有配置环境信息,SqlSessionFactoryBean会创建只有的mybatis环境
方式二:通过继承SqlSessionDaoSupport类实现
修改实现类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{ public User getUser(int id){ UserMapper userMapper=(UserMapper)getSqlSession().getMapper(UserMapper.class); return userMapper.getUser(id); } }
修改bean配置
<bean id="userDao2" class="com.deng.dao.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
测试
public void test1(){ ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); UserMapper userMapper=(UserMapper)context.getBean("userDao2"); User user=userMapper.getUser(1); System.out.println(user); }
声明式事务
编程式事务:将事务管理代码嵌入到业务方法中
声明式事务:将事务管理代码与业务方法分离,以声明的方式管理事务。通过Spring AOP框架支持声明式事务管理
在spring配置文件中,导入头文件tx
xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
配置事务通知
<!--配置事务通知--> <tx:advice id="txAdivce" transaction-manager="transactionManager"> <tx:attributes> <!--配置哪些方法需要使用什么的事务,配置事务的传播特性--> <tx:method name="gerUser" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
事务的传播特性就是多个事务方法相互调用时,事务是如何在这些方法间传播的,Spring支持7种事务传播行为
- propagation_required:当前没有事务就新建一个事务,如果已存在一个事务,就加入到该事务中
- propagation_supports:支持当前事务,如果没有事务,就以非事务的方式执行
- propagation_mandatory:使用当前事务,如果没有事务,抛出异常
- propagation_required_new:新建事务,如果当前存在事务,将当前事务挂起
- propagation_not_supported:以非事务的方式执行操作,如果当前存在事务把当前事务挂起
- propagation_never:以非事务的方式执行操作,如果当前事务存在,抛出异常
- propagation_nested:如果当前存在事务,则嵌套的事务内执行,如果当前没有事务,则执行与 propagation_required类似的操作
spring默认的事务传播行为是propagation_required
织入事务
<!--配置aop织入事务--> <aop:config> <aop:pointcut id="txPointcut" expression="execution(* com.deng.dao.*.* (..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>