Spring学习
优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
IOC本质
控制反转(IOC)是一种设计思想,DI(依赖注入)是实现IOC的一种方法。使用面向对象编程时,对象的创建与对象中的依赖关系完全因编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方:获得依赖对象的方式反转了。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependency Injection,DI)
HelloSpring
导入jar包
在父工程的pom中导入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
实体类
package entity;
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("hello,"+name);
}
}
beans.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对象 , 由Spring创建和管理-->
<bean id="hello" class="entity.Hello">
<property name="name" value="牛牛"/>
</bean>
</beans>
测试
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
think
- Hello对象由Spring创建
- Hello对象的属性是由Spring容器设置的
- 控制:谁来控制对象的创建,传统应用程序电动对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
- 反转:程序本身不创建对象,而变成被动的接收对象。
- 依赖注入:程序本身不创建对象,而变成被动的接收对象。
IOC创建对象方式
通过无参构造方法来创建
public class User {
private String name;
public User() {
System.out.println("user无参构造方法");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
<bean id="user" class="entity.User">
<property name="name" value="牛牛"/>
</bean>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在执行getBean的时候, user已经创建好了 , 通过无参构造
User user = (User) context.getBean("user");
//调用对象的方法 .
user.show();
}
通过有参构造方法来创建
public class UserT {
private String name;
public UserT(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
三种方式写xml
<!--根据index参数下标设置-->
<bean id="userT" class="entity.UserT">
<!--index指构造方法,下标从0开始-->
<constructor-arg index="0" value="哈哈index"/>
</bean>
<!--根据参数名字设置-->
<bean id="userT" class="entity.UserT">
<!--name指参数名-->
<constructor-arg name="name" value="哈哈name"/>
</bean>
<!--根据参数类型设置-->
<bean id="userT" class="entity.UserT">
<constructor-arg type="java.lang.String" value="哈哈type"/>
</bean>
测试
@Test
public void testT(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserT user = (UserT) context.getBean("userT");
user.show();
}
Spring配置
别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
Bean的配置
Bean的配置
<!--bean就是java对象,由Spring创建和管理-->
<!--
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
import
团队合作通过import实现
<import resource="{path}/beans.xml"/>
依赖注入(Dependency Injection,DI)
依赖:指Bean对象的创建依赖于容器,Bean对象的依赖资源
注入:指Bean对象所依赖的资源,由容器来设置和装配
- 构造器注入
- Set注入【重点】
Set注入
要求被注入的属性,必须有set方法。
<?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="address" class="entity.Address">
<property name="address" value="广州"/>
</bean>
<bean id="student" class="entity.Student">
<!--常量注入-->
<property name="name" value="牛牛"/>
<!--Bean注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<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="111111"/>
<entry key="银行卡" value="222222"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>lol</value>
<value>bob</value>
</set>
</property>
<!--null注入-->
<property name="wife"><null/></property>
<!--properties注入-->
<property name="info">
<props>
<prop key="姓名">牛牛</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
p名命和c名命注入
- p名命空间注入:在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="entity.User" p:name="牛牛" p:age="20"/>
- c 命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="entity.User" c:name="牛牛" c:age="20"/>
Bean的作用域
- Singleton
在Spring IOC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
- Prototype
每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new xxxBean()
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
- Request
每次http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
- Session
同一个http session共享一个bean,不同session使用不同bean,仅适用于WebApplicationContext环境
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
自动装配
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
- 组件扫描:spring会自动发现应用上下文中创建的bean
- 自动装配:spring自动满足bean之间的依赖,也就是我们说的IOC/DI
Bean的自动装配
- byName 按名称自动装配:autowire=“byName”
当一个bean节点带有autowire byName的属性时。
- 查找其类中所有的set方法名,获得set去掉并且首字母小写的字符串
- 去Spring容器中寻找是否有此字符串名称id的对象
- 如果有,就取出注入;如果没有,就报空指针异常
- byType 按类型自动装配:autowire=“byType”
要保证同一类型的对象,在Spring容器中唯一。
使用注解
- 在Spring配置文件中引入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/>
Autowired
- 按类型自动转配(byType),不支持id匹配
- 要导入spring-aop的包!【不过导入了springmvc包就包括了】
@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
Qualifier
- 加上@Qualifier则可以根据byName的方式自动装配
- 不能单独使用,要搭配@Autowired
<bean id="dog1" class="entity.Dog"/>
<bean id="dog2" class="entity.Dog"/>
<bean id="cat1" class="entity.Cat"/>
<bean id="cat2" class="entity.Cat"/>
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
Resource
- 如有指定的name属性,先按该属性进行byName方式查找装配
- 再进行默认的byName进行
- 如都不成功,则按byType
public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
@Autowired与@Resource
- 都可以用来装配bean。都可以写在字段上或者写在setter方法上。
- 作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName
使用注解开发
- 导入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">
</beans>
Bean的实现
- 配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="entity"/>
- 在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
public String name = "哈哈";
}
属性注入
- 可以不提供set方法,直接在直接名上添加@value(“值”)
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("哈哈")
// 相当于配置文件中 <property name="name" value="哈哈"/>
public String name;
}
- 如果提供了set方法,在set方法上添加@value(“值”)
@Component("user")
public class User {
public String name;
@Value("哈哈")
public void setName(String name) {
this.name = name;
}
}
衍生注解
功能一样,可以实现更好的分层。
- @Controller:web层
- @Service:service层
- @Repository:dao层
小结
- 如果不扫描包,就要手动配置bean
- 如果不加注解驱动,则注入的值为null
基于Java类进行配置
配置类
@Configuration//代表这是一个配置类
public class MyConfig {
@Bean
public User user(){
return new User();
}
}
实体类
@Component
public class User {
@Value("哈哈")
public String name;
}
测试
@Test
public void Test(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
导入合并其他配置类
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
代理模式
静态代理
抽象角色用抽象类或者用接口实现,真实角色是被代理的对象,代理角色是代理真实对象,再加一些附属的操作,客户使用代理对象来进行一些操作。
好处:
- 使真实角色更加纯粹,不去关注一些公共的事情
- 公共业务由代理来完成,实现了业务的分工
- 公共业务发生扩展时变得更加集中和方便
缺点: - 类越多代理越多,工作量变大,开发效率变低
动态代理
- 动态代理类是动态生成的,静态代理类是提前写好的
JDK的动态代理的两个核心的类:
- InvocationHandler:调用处理程序
- Proxy:代理
核心:一个动态代理,一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口!
AOP
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。可以对业务逻辑的各个部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发的效率。
AOP在Spring中的作用
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
-
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
-
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
-
目标(Target):被通知对象。
-
代理(Proxy):向目标对象应用通知之后创建的对象。
-
切入点(PointCut):切面通知 执行的 “地点”的定义。
-
连接点(JointPoint):与切入点匹配的执行点
用Spring实现AOP
导入jar包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 通过Spring API实现
<?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">
<!--注册bean-->
<bean id="userService" class="demo2.UserServiceImpl"/>
<bean id="beforeLog" class="demo2.BeforeLog"/>
<bean id="afterLog" class="demo2.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* demo2.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 自定义类来实现AOP
编写切入类
package demo2;
public class DiyPointcut {
public void before(){
System.out.println("=======方法执行前========");
}
public void after(){
System.out.println("======方法执行后=========");
}
}
spring中配置
<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="demo2.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二种方式:使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* demo2.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>
- 注解实现
编写一个注解实现的增强类
package demo2;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* demo2.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前========");
}
@After("execution(* demo2.UserServiceImpl.*(..))")
public void after(){
System.out.println("----------方法执行后=========");
}
}
在spring配置文件中注册bean,并增加支持注解的配置
<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="demo2.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
整合MyBatis
导入相关jar包
junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
mysql-connector-java
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
spring相关
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
aspectJ AOP 织入器
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
mybatis-spring整合包 【重点】
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
Maven资源过滤问题
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
编写配置文件
beans.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">
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="0315"/>
</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:dao/*.xml"/>-->
</bean>
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--注册Bean实现-->
<bean id="userDao" class="dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
mybatis-config.xml
<?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="entity"/>
</typeAliases>
<mappers>
<package name="dao"/>
</mappers>
</configuration>
UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<select id="selectUser" resultType="User">
select * from user
</select>
</mapper>
UserDao
package dao;
import entity.User;
import java.util.List;
public interface UserDao {
public List<User> selectUser();
}
UserDaoImpl
package dao;
import entity.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserDaoImpl implements UserDao{
//sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserDao mapper = sqlSession.getMapper(UserDao.class);
return mapper.selectUser();
}
}
Test
import dao.UserDao;
import entity.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = context.getBean("userDao", UserDao.class);
List<User> users = userDao.selectUser();
System.out.println(users);
}
}
代码实现
声明式事务
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
四个属性:
- 原子性
- 一致性
- 隔离性
- 持久性
- 编程式事务管理是将事务管理代码嵌到业务方法中来控制事务的提交和回滚;
- 声明式事务是将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,将事务管理作为横切关注点,通过aop方法模块化。Spring中通过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">
事务管理器就是Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
JDBC事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置好事务管理器后我们要去配置事务的通知
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
配置AOP
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
要配置事务的原因:
- 如果不配置就要我们手动提交控制事务
- 涉及到数据的一致性问题