基本概念
一些框架整合:
- SSH:Struct2+Spring+Hibernate
- SSM:SpringMvc+Sping+Mybatis
Spring特点:
- 轻量级、非入侵式、开源免费框架
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务处理,对框架整合支持
组成
IOC理论
此时有一个接口UserDao和一些实现类,用户可能会调用不同的实现类
如果按照一般的设计理念来说,会在UserServiceImpl中指定实现类
1 | private UserDao userDao = new UserDapMysqlImpl; |
如果这样做,每次用户变更需求需要在业务层中更改代码,十分麻烦,此时可以按照IOC思想,使用DI(依赖注入)的实现方式,将控制权转移到用户手中
1 | private UserDao userDao; |
此时用户在指定具体的实现方法时只需要传入特定的实现类,即可调用不同实现类中的方法,而程序员不需要再更改业务层中的代码。
MyTest.java
部分代码
1 | UserSercive userService = new UserServiceImpl; |
本质区别
之前程序控制权在业务层手中,IOC中程序控制权在用户手中
解耦的过程
IOC本质
控制反转IoC是一种设计思想,DI(依赖注入)是实现IoC的一种方法,没有IoC的程序中,我们使用面向对象编程,对象的创建与对象之间的依赖完全硬编码在程序中,对象的创建程序由程序自己控制,控制反转后将对象的创建转移给第三方。
控制反转是一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,实现方式为依赖注入。
HelloSpring
导入maven配置(webmvc整合了多个依赖,避免了多次导入的重复)
1
2
3
4
5<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>实体类
Hello.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String toString() {
return "hello{" +
"str='" + str + '\'' +
'}';
}
}配置文件
beans.xml
1
2
3
4
5
6
7
8
9
10
<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="myHello" class="com.lan5th.pojo.Hello">
<property name="str" value="Hello world!"/>
</bean>
</beans>测试类
HelloTest.java
1
2
3
4
5
6
7public class HelloTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello myHello = (Hello) context.getBean("myHello");
System.out.println(myHello);
}
}
IOC创建对象的方式
默认无参构造创建对象
同上
<property name="str" value="Hello world!"/>
用法如果使用有参构造创建对象
下标赋值
1
2
3<bean id="myHello" class="com.lan5th.pojo.Hello">
<constructor-arg index="0" value="Hello world!"/>
</bean>类型赋值,不推荐使用(多个同类型值难以处理)
1
2
3<bean id="myHello" class="com.lan5th.pojo.Hello">
<constructor-arg type="java.lang.String" value="Hello world!"/>
</bean>参数名赋值
1
2
3<bean id="myHello" class="com.lan5th.pojo.Hello">
<constructor-arg name="str" value="Hello world!"/>
</bean>
在配置文件加载时,容器中所有注册的对象都已经被实例化了一次
Spring配置
Alias
配置别名之后新旧两个名字都可以正常获取到对象
1 | <alias name="user" alias="userNew"/> |
但一般取别名时不适用alias,而是直接在bean里面配置name属性
Bean
1 | <!-- |
Import
impot一般用于团队开发使用,可以将多个配置文件合并导入一个总的配置文件
假设项目中有三人进行开发,这三个人负责不同类的开发,不同的类注册在不同的beans中,我们可以利用import将所有的beans.xml合并为一
application.xml
1 | <import resource="beans1.xml"/> |
DI依赖注入
构造器注入
1
2
3<bean id="myHello" class="com.lan5th.pojo.Hello">
<constructor-arg index="0" value="Hello world!"/>
</bean>与之前相同,不再复述
Set方式注入
1
2
3
4
5
6
7
8
9//数据结构
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44<bean id="myHello" class="com.lan5th.pojo.Hello">
<property name="name" value="张三"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<property name="hobbys">
<list>
<value>听歌</value>
<value>吃饭</value>
<value>看电影</value>
</list>
</property>
<property name="card">
<map>
<entry key="身份证" value="123"/>
<entry key="学号" value="1234"/>
</map>
</property>
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
<value>MO</value>
</set>
</property>
<property name="wife">
<null/>
</property>
<property name="info">
<props>
<prop key="driver">xxx</prop>
<prop key="url">xxx</prop>
<prop key="username">lan5th</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>拓展方式注入
p命名空间对应set方法、c命名空间对应construct方法(注意使用前需要提前添加依赖)
1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloBean" class="com.lan5th.pojo.Hello" p:str="hello"/>
</beans>1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloBean" class="com.lan5th.pojo.Hello" c:_0="hello1"/>
</beans>
Bean作用域
1 | <bean id="helloBean" class="com.lan5th.pojo.Hello" p:str="hello" scope="singleton"/> |
- 单例模式(Spring默认)
- 原型模式:每次从容器中get时都会产生一个新对象
- 其余的request、session、application一般在web开发中用到
Bean自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
Spring三种装配方法
- xml显式配置
- java中显式配置
- 隐式自动装配bean
自动转配类型
分别根据set方法中提供的变量名和变量类型自动在上下文中寻找对应实体进行装配
1 | public class Hello { |
ByName
1
2
3
4<bean id="myHello" class="com.lan5th.pojo.Hello" autowire="byName">
<property name="str" value="Hello world!"/>
</bean>
<bean id="dog" class="com.lan5th.pojo.Dog"/>ByType
1
2
3
4<bean id="myHello" class="com.lan5th.pojo.Hello" autowire="byType">
<property name="str" value="Hello world!"/>
</bean>
<bean id="dog" class="com.lan5th.pojo.Dog"/>
byName时需要保证所有beanid唯一,而byType需要保证所有bean的class唯一
使用注解自动装配
jdk1.5,Spring2.5之后就开始支持注解开发了
使用注解前:
- 导入约束
xmlns:context
约束 - 配置注解支持
<context:annotation-config/>
1 |
|
@Autowired
- 可以在属性或set方法上使用
- 使用Autowired后我们可以不用编写set方法了,前提是这个自动装配的属性在IOC(Spring)容器中存在,且符合byName规则
拓展:
@Nullable
注解表示该字段可以为null@Autowired
注解中有一个required参数,表示该对象是否为必需,与@Nullable
相似1
2
3public Autowired {
boolean required() default true;
}1
2
3
4
5
6public class Hello {
private String str;
private Dog dog;
//...
}
如果Autowired
自动装配的环境比较复杂,自动装配无法通过一个注解完成时,可以增加@Qualifier(value = "")
去配合@Autowired
的使用,指定一个唯一的bean对象注入
1 | <bean id="myHello" class="com.lan5th.pojo.Hello"> |
1 | public class Hello { |
@Resource
和@autoWired
类似,但是是java原生提供的注解
- 都是用来自动装配的,都可以放在属性字段或set方法上
@Autowired
默认通过byType方式,有多个重复Type再通过byName方式查找@Resource
默认通过byName方式,有多个重复id再通过byType方式查找
使用注解开发
xml功能更齐全,适用于任何场合,维护简单方便,而注解适用于简单的类,且只能作用于本类上
一般采用:
- xml管理bean
- 注解进行属性注入
Spring4后要使用注解开发必须导入aop包,并导入<context:annotation-config/>
约束
1 | <!--扫描特定包,使其中的注解生效--> |
属性注入
可以在属性上加,也可以在set方法上加
1 |
|
注解衍生
基于MVC三层架构
- dao层:
@Repository
- service层:
@Service
- controller层:
@Controller
这三个注解跟@Component
功能类似,将某个类注册到Spring中,装配Bean,但区分于不同的开发模块
自动装配配置
1 | @Autowired:自动通过类型、名字装配 |
作用域
1 | //prototype:原型模式 |
使用Java的方式配置Spring
现在完全不使用Spring的xml配置
JavaConfig是Spring的一个字项目,在Spring4之后成为了核心功能
配置类Lan5thConfig.java
1 | //配置类注解 |
Hello.java
1 |
|
测试类ConfigTest.java
1 | public class ConfigTest { |
这种纯java的配置方式在SpringBoot中随处可见
代理模式
作为SpringAOP的底层
静态代理
角色分析:
抽象角色:一般使用接口或抽象类实现
1
2
3public interface Rent{
public void rent();
}真实角色:被代理的角色
1
2
3
4
5public class Host implements Rent{
public void rent(){
System.out.println("房东出租房子");
};
}代理角色:代理真实角色,一般代理后会做一些附属操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class Proxy implements Rent{
private Host host;
public Proxy(){}
public Proxy(Host host){
this.host = host;
}
public void rent(){
seeHouse();
host.Rent();
hetong();
fare();
}
public void seeHouse(){
System.out.println("中介带领看房");
}
public void hetong(){
System.out.println("签租借合同");
}
public void fare(){
System.out.println("收中介费");
}
}客户:访问代理对象的角色
1
2
3
4
5
6
7public class Client{
public void Test(){
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
代理模式优点:
- 使真实角色的操作更加纯粹,不再关心一些公共业务
- 公共业务交给代理角色,实现业务分工
- 公共业务发生扩展的时候方便集中管理
缺点:
- 一个真实角色对应一个代理角色,代码量翻倍,开发效率降低
动态代理
角色和静态代理一样,区别是动态代理类使动态生成的,不是直接写好的
- 基于接口–jdk动态代理【此处使用】
- 基于类–cglib
- java字节码实现–JAVAssist
使用到两个类:
Proxy
:动态生成代理对象Proxy.newProxyInstance()
传入三个参数:- ClassLoader:InvocationHandler实现类位置
- Interface:需要实现的接口
- InvocationHandler:InvocationHandler类对象,这里直接指定this
InvocationHandler
:调用处理程序并返回结果只有一个函数
invoke()
,得到代理对象后执行具体方法时自动运行,用过method.invoke()
指定代理的类和参数
ProxyInvocationHandler.java
1 | public class ProxyInvocationHandler implements InvocationHandler { |
ProxyTest.java
1 | public class ProxyTest { |
执行结果
优点:
- 包括静态代理的所有优点
- 一个动态代理代理的是一个接口,一般对应一类业务
- 一个动态代理可以代理多个类,只需要这些类实现同一个接口,减少了静态代理的缺点
AOP
只有IOC容器中的组件才能获得AOP功能,在获取实例时必须从容器中获取
面向切面编程,是OOP(面向对象)的延续,思想和动态代理模式类似,降低耦合性,提高可重用性和开发效率
预先导包
1 | <dependency> |
方式一:使用Spring自带接口
接口Animal.java
1 | public interface Animal { |
实现类Dog.java
1 | public class Dog implements Animal { |
执行前输出LogBefore.java
1 | public class LogBefore implements MethodBeforeAdvice { |
执行后输出LogAfter.java
1 | public class LogAfter implements AfterReturningAdvice { |
配置beansaop.xml
1 |
|
测试类AopTest.java
1 | public class AopTest { |
方式二:自定义类
自定义类DIYLog.java
1 | public class DIYLog { |
重新配置aop.xml
1 | <bean id="diy" class="com.lan5th.service.DIYLog"/> |
方式三:注解实现
aop注解参数格式:访问修饰符 返回值类型 方法全类名(参数)
,如:
- 指定某个方法
execution(public int com.lan5th.service.calculate(int, int))
- 指定所有方法
execution(* com.lan5th.service.calculate.*(..))
==切面类和被切入的类都需要被注入到容器中==
带注解的自定义类AnnotationLog.java
1 | //告诉spring这是一个切面类 |
重新配置aop.xml
,或给配置类上添加@EnableAspectJAutoProxy
1 | <bean id="AnnAop" class="com.lan5th.service.AnnotationLog"/> |
常用方法
1 |
|
需要注意一下环绕方法,需要手动放行
通过JoinPoint参数可以获取到被切入方法的许多信息
1 |
|
整合Mybatis
搭建Mybatis环境
步骤:
导入jar包
junit
mtbatis
mysql
spring
spring-jdbc【新增】
mybatis-spring【新增】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--lombok自选-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
编写配置文件
测试
Mybatis-Spring
步骤:
编写数据源
编写SqlSessionFactory
编写SqlSessionTemplate
SqlSessionTemplate是SqlSession的一个实现,在代码中可以无缝替代之前的SqlSession
编写实现类
将实现类注入到Spring中
User.java
,UserMapper.java
和UserMapper.xml
与之前相同
方式一:直接实例化SqlSessionTemplate对象
新增实现类UserMapperImpl.java
1 | public class UserMapperImpl implements UserMapper{ |
精简化mybatis-config.xml
1 |
|
Spring核心文件spring-dao.xml
1 |
|
测试类MSTest.java
1 | public class MSTest { |
方法二:继承SqlSessionDaoSupport类
新增实现类UserMapperImpl2.java
1 | public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{ |
简化spring-dao.xml
1 |
|
声明式事务
配置文件方式
让mybatis通过spring来管理事务,借由DataSourceTransactionManager类实现
配置spring-dao.xml
注意需要导入aop和tx的依赖
1 |
|
七种事务传播方式
如果不在Spring中实现事务,就需要在代码中手动配置事务
注解方式
配置数据源DataSource
主类上添加
@EnableTransactionManagement
需要转为事务操作的方法上添加
@Transactional
配置事务管理器来管理事务
1
2
3
4
5
public PlatformTransactionManager transactionManager(){
//这里传入配置好的数据源
return new DataSourceTransactionManager(datasource());
}