Spring(一)

共两部分

参考

https://www.bilibili.com/video/BV1WE411d7Dv

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core

Spring

简介

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

◆目的:解决企业应用开发的复杂性

◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

◆范围:任何Java应用

Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core

下载:https://repo.spring.io/release/org/springframework/spring/

Github:https://github.com/spring-projects/spring-framework

Spring总结起来优点如下:

  • 低侵入式设计,代码的污染极低。
  • 独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺。
  • Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦。
  • Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用。
  • Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
  • Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部。

Spring配置

别名

1
2
<!-- 别名 -->
<alias name="user" alias="dgsdfgfdg"/>

Bean 配置

1
2
3
4
5
6
7
8
<!-- 
id 唯一标识符
class bean 对象的全限定包 包名 + 类名
name 别名,可以有多个
-->
<bean id="user" class="com.wnh.pojo.User" name="user1,user2">
<constructor-arg name="name" value="老杨"/>
</bean>

import

一般用于团队开发,将多个配置文件导入合并

IOC

IoC也称为依赖注入(DI)。在此过程中,对象仅通过构造函数参数,工厂方法的参数或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即,与它们一起使用的其他对象) 。然后,容器在创建bean时注入那些依赖项。此过程从根本上讲是通过使用类的直接构造或诸如服务定位器模式之类的控件来控制其依赖项的实例化或位置的bean本身的逆过程(因此称为Control Inversion)。

Spring的工作原理的高级视图

Spring的工作原理的高级视图

感受IOC

导入下面这个会依赖地导入一些必要包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>

传统开发过程

  • UserDao接口
1
2
3
public interface UserDao {
void getUser();
}
  • UserImpl实现
1
2
3
4
5
6
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据");
}
}
  • UserService接口
1
2
3
public interface UserService {
void getUser();
}
  • UserService实现
1
2
3
4
5
6
7
8
9
10
public class UserServiceImpl implements UserService {

private UserDao userDao = new UserDaoImpl();

@Override
public void getUser() {
userDao.getUser();

}
}
  • 测试
1
2
3
4
5
6
7
public class MyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();

userService.getUser();
}
}

若这时加入新的 dao 实现类

UserMysqlImpl

1
2
3
4
5
6
public class UserMysqlImpl implements UserDao{
@Override
public void getUser() {
System.out.println("MySQL获取");
}
}

UserOracleImpl

1
2
3
4
5
6
public class UserOracleImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Oracle获取");
}
}

我们必须修改 Service 方法

1
2
3
4
5
6
7
8
9
10
11
12
public class UserServiceImpl implements UserService {

// private UserDao userDao = new UserDaoImpl();
private UserDao userDao = new UserMysqlImpl();
// private UserDao userDao = new UserOracleImpl();

@Override
public void getUser() {
userDao.getUser();

}
}

但这样会加大开发难度,而且不能专心的在一方面搞下去

这时我们可以这样处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserServiceImpl implements UserService {

private UserDao userDao ;

// 通过 SET 动态实现值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public void getUser() {
userDao.getUser();

}
}

修改后测试

1
2
3
4
5
6
7
8
9
public class MyTest {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();

userService.setUserDao(new UserOracleImpl());

userService.getUser();
}
}

这样的话我们就可以动态实现值的注入

配置元数据

以下示例显示了基于XML的配置元数据的基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>

IOC容器赋值:如果是简单类型(8个基本+String),value;如果是对象,ref=“需要引入的id值”

id属性是标识单个bean定义的字符串。

class属性定义Bean的类型,并使用完全限定的类名。

实例化容器

1
2
3
4
5
6
7
8
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

Bean命名约定

约定是在命名bean时将标准Java约定用于实例字段名称。也就是说,bean名称以小写字母开头,并从那里用驼峰式大小写。这样的名字的例子包括accountManageraccountServiceuserDaologinController,等等。

一致地命名Bean使您的配置更易于阅读和理解。另外,如果您使用Spring AOP,则在将建议应用于名称相关的一组bean时,它会很有帮助。

示例:

pojo.Hello.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Hello {
private String str;

public String getStr() {
return str;
}

public void setStr(String str) {
this.str = str;
}

@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">

<!-- 使用 Spring 来创建对象,在 Spring 中这些都成为 Bean -->

<bean id="hello" class="com.wnh.pojo.Hello">
<property name="str" value="Spring"/>
</bean>

</beans>

测试:

1
2
3
4
5
6
7
8
9
public class Test1 {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象都交由 Spring 管理,我们要使用,直接去取
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}

将上面 User dao Service 修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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">

<!-- 使用 Spring 来创建对象,在 Spring 中这些都成为 Bean -->

<bean id="hello" class="com.wnh.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
<bean id="mysqlImpl" class="com.wnh.dao.UserMysqlImpl"/>
<bean id="oracleImpl" class="com.wnh.dao.UserOracleImpl"/>

<bean id="userServiceImpl" class="com.wnh.service.UserServiceImpl">
<property name="userDao" ref="mysqlImpl"/>
</bean>
</beans>

再测试:

1
2
3
4
5
6
7
8
9
10
11
public class Test1 {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象都交由 Spring 管理,我们要使用,直接去取
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
userServiceImpl.getUser();
}
}

依赖注入

基于构造函数的依赖注入

1、无参构造创建对象,默认

2、有参构造方式

Spring无法确定值的类型,因此在没有帮助的情况下无法按类型进行匹配。考虑以下类别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package examples;

public class ExampleBean {

// Number of years to calculate the Ultimate Answer
private int years;

// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;

public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}

构造函数参数类型匹配

在上述情况下,如果通过使用type属性显式指定构造函数参数的类型,则容器可以使用简单类型的类型匹配。如下例所示:

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>

构造函数参数索引

您可以使用该index属性来明确指定构造函数参数的索引,如以下示例所示:

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>

除了解决多个简单值的歧义性之外,指定索引还可以解决歧义,其中构造函数具有两个相同类型的参数。

构造函数参数名称

您还可以使用构造函数参数名称来消除歧义,如以下示例所示:

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>

请记住,要立即使用该功能,必须在启用调试标志的情况下编译代码,以便Spring可以从构造函数中查找参数名称。如果您不能或不想使用debug标志编译代码,则可以使用 @ConstructorProperties JDK批注显式命名构造函数参数。

1
2
3
4
5
6
7
8
9
10
11
12
package examples;

public class ExampleBean {

// Fields omitted

@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}

基于Setter的依赖注入

复杂类型

1
2
3
4
5
6
7
8
9
10
11
public class Address {
private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}

测试对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String,String> cards;
private Set<String> games;
private Properties info;
private String wife;

。。。

}

beans.xml

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
45
46
47
48
49
50
51
52
53
54
55
<bean id="address" class="com.wnh.pojo.Address"/>

<bean id="student" class="com.wnh.pojo.Student">
<!-- 普通注入 value -->
<property name="name" value="老杨"/>
<!-- Bean 注入 ref -->
<property name="address" ref="address"/>
<!-- 数组注入 array -->
<property name="books">
<array>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
<value>红楼梦</value>
</array>
</property>
<!-- list注入 list -->
<property name="hobbies">
<list>
<value>玩游戏</value>
<value>看手机</value>
<value>听音乐</value>
<value>看电影</value>
</list>
</property>
<!-- map注入 map -->
<property name="cards">
<map>
<entry key="身份证" value="124"/>
<entry key="校园卡" value="125"/>
<entry key="手机卡" value="126"/>
<entry key="银行卡" value="127"/>
</map>
</property>
<!-- set注入 set -->
<property name="games">
<set>
<value>LOL</value>
<value>CS</value>
<value>CF</value>
</set>
</property>
<!-- 空值注入 null -->
<property name="wife">
<null/>
</property>
<!-- 属性注入 props -->
<property name="info">
<props>
<prop key="学号">112</prop>
<prop key="性别"></prop>
<prop key="宿舍">101</prop>
</props>
</property>
</bean>

扩展方式注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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:c="http://www.springframework.org/schema/c"
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">

<!-- p命名空间注入,可以直接注入属性值 property -->
<bean id="hello" class="com.wnh.pojo.Hello" p:str="Spring"/>

<!-- c命名空间注入,通过构造器注入 constructor-args -->
<bean id="hello0" class="com.wnh.pojo.Hello" c:str="Spring"/>

</beans>

测试

1
2
3
4
5
6
7
8
9
10
11
12
public class Test1 {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象都交由 Spring 管理,我们要使用,直接去取

Hello hello = (Hello) context.getBean("hello");
Hello hello0 = (Hello) context.getBean("hello0");
System.out.println(hello);
System.out.println(hello0);
}
}

需要导入

1
2
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"

共两部分