Spring初识
文章目录
1 认识
Spring:给软件行业带来春天
现在的java 开发,说白了就是基于Spring的
Spring目的:解决企业应用开发的复杂性
现在学习Spring,为了之后学习SpringMVC,SpringBoot作为基础
核心:Spring中的IOC与AOP
学习之地:
- 官网:http://spring.io/
- Spring的学习文档,API文档
- GitHub:https://github.com/spring-projects/spring-framework
2 IOC基础
IOC:控制反转(一种思想)
初识案例:
-
UserMapper 接口
-
UserMapperImpl 实现类
-
UserService 业务接口
-
UserServiceImpl 业务实现类
首先建立一个UserMapper的接口,里面写入一方法:
public interface UserMapper {
public void getUser();
}
再,建立实现接口的类,先建立一个:
public class UserMapperImpl implements UserMapper{
public void getUser() {
System.out.println("一般获取用户数据");
}
}
接着建立一个UserService接口,写入方法
public interface UserService {
public void getUser();
}
写它对应的实现接口类
public class UserServiceImpl implements UserService {
private UserMapper userMapper = new UserMapperImpl();
public void getUser() {
userMapper.getUser();
}
}
进行测试:
public class Mytest {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
}
}
//打印出一般获取用户数据
当我们在建立了更多的UserMapperImpl类,每次都需要在底层代码里,
比如,建立了一个
public class UserMapperSqlImpl implements UserMapper {
public void getUser() {
System.out.println("sql获取用户数据");
}
}
需要在User ServiceImpl中,要new一个UserMapperSqlImpl(),在测试的时候才会输出我们想要的"sql获取用户数据";或者可以这样解决:
//设置一个set方法
public class UserServiceImpl implements UserService {
//private UserMapper userMapper = new UserMapperImpl();
//private UserMapper userMapper = new UserMapperSqlImpl();
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public void getUser() {
userMapper.getUser();
}
}
测试的时候就可以这样写:
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();
userService.getUser();
userService.setUserMapper(new UserMapperOracleImpl());
userService.getUser();
userService.setUserMapper(new UserMapperSqlImpl());
userService.getUser();
}
用一张图来解释我们在干什么:
我们的目的就是让用户拿到主动权,他需要什么就调用什么.
像在没有使用set方法之前,程序需要主动的创建需要的对象,但是用了set之后,程序就不主动创建了,而是接受用户所需的对象.
这样的思想,程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC 的原型!
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3 HelloSpring
首先建立一个空的Maven项目
1、导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2、编写代码,分析
建立一个Hello类,通过show()方法把hello+s所传入参数打印出来
public class Hello {
private String name;
public void setName(String name){
this.name=name;
}
public void show(){
System.out.println("Hello"+name);
}
}
在resources中建立applicationContext.xml文件,并需要将官网上的配置文件内容拷贝下来
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans.xsd">
6
7 <!--bean就是java对象 , 由Spring创建和管理-->
8 <bean id="hello" class="com.feng.pojo.Hello">
9 <property name="name" value="Spring"/>
10 </bean>
11
12 </beans>
在测试类中进行测试
@Test
public void test2(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
这样会打印:
解释:
-
hello 对象是由Spring创建的
-
hello 对象的属性是由Spring容器设置的
-
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .
-
反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
-
依赖注入 : 就是利用set方法来进行注入的.
到目前,彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改
4 IOC创建对象的方式
使用情况:
1、无参构造创建,需要类中有set方法
2、有参构造创建,需要类中有有参构造方法,构造器注入,也叫C注入
3、有参构造创建拓展方法
测试步骤:
1、创建实体类,设置set方法
2、使用Spring创建bean
3、测试结果(有参构造创建,无参创建,删除set方法测试)
详细内容:
User类:
public class User {
private String name;
public User(){
System.out.println("对象被创建了");
}
// public void setName(String name) {
// this.name = name;
// }
//构造方法
public User(String name) {
this.name = name;
}
public void show(){
System.out.println("hello,"+name);
}
}
在application Context配置文件中进行配置:
<!--类中有set方法-->
<bean id="user" class="com.feng.pojo.User">
<property name="name" value="xiaoming1"/>
</bean>
<!--类中无set方法后,建立有参构造方法-->
<bean id="user" class="com.feng.pojo.User">
<constructor-arg name="name" value="xiaoming2"/>
</bean>
<!--拓展方法-->
<bean id="user" class="com.feng.pojo.User">
<!--根据参数对应的次序,第一个参数的index从0开始-->
<constructor-arg index="0" value="xiaoming3"/>
<!--当参数只有一个时,可以根据类型来;当有多个参数且类型不一致时,不好使用-->
<constructor-arg type="java.lang.String" value="xiaoming4"/>
</bean>
测试类中:
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println("===========");
user.show();
}
测试1(类中有set方法):
测试2(类中无set方法后,建立有参构造方法):
测试(拓展方法):
5 Spring配置
在applicationContext.xml中
1、alias别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>
2、Bean
bean标签中
id:bean的唯一标识符,对象名
name:就是bean的别名
class:bean的全类名
<bean name="hello hello2 hello3" class="com.feng.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
3、import,在多人协作中
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的applicationContext.xml合并为一个总的
<import resource="Student.xml"/>
<import resource="spring_autowire.xml"/>
<import resource="SpringContext.xml"/>
4 scope作用域
prototype:每次创建新对象
singleton:在容器中只有一个对象(默认)
request/session在web 中使用
<bean id="student" class="com.feng.pojo.Student" scope="singleton"></bean>
6 属性注入
实现一个小程序:
1、建立实体类
2、通过编写xml配置注入student对象
3、测试结果注入成功
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
private String wife;
private Properties info;
}
//要么加上lombok注解
//要么重写set方法与toString方法
编写student.xml文件:(针对student类中不同属性进行注入),且这个xml文件要import至applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.feng.pojo.Address">
<property name="address" value="西安"/>
</bean>
<bean id="student" class="com.feng.pojo.Student" scope="singleton">
<!--第一种,普通值注入,value-->
<property name="name" value="fff"/>
<!--第二种,Bean注入,ref-->
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--List-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!--Map-->
<property name="map">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<!--Set-->
<property name="set">
<set>
<value>s1</value>
<value>s2</value>
<value>s3</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="year">2020</prop>
<prop key="gender">男</prop>
<prop key="name">f</prop>
<prop key="pwd">00000</prop>
</props>
</property>
</bean>
</beans>
测试类:
@Test
public void test4(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
测试结果:
7 自动装配
前面学会如何使用元素来声明 bean 和通过使用 XML 配置文件中的
< constructor-arg>和< property>元素来注入 。
Spring 容器可以在不使用< constructor-arg>和< property>元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。
Spring 自动装配 ‘byName’
autowire=“byName” 自动寻找和属性相关的bean,本质为set方法
这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
进行测试:
程序展示一个人有两个宠物
实体类:User,Dog,Cat
public class User {
private Cat cat;
private Dog dog;
private String str;
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public void setStr(String str) {
this.str = str;
}
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
在autowire.xml中进行配置,须导入至applicationContext.xml中
<bean id="cat" class="com.feng.entity.Cat"></bean>
<bean id="dog" class="com.feng.entity.Dog"></bean>
<bean id="uu"
class="com.feng.entity.User"
autowire="byName">
<property name="cat" ref="cat"></property>
<property name="dog" ref="dog"></property>
<property name="str" value="xiaoming"></property>
</bean>
进行测试:
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
com.feng.entity.User user = context.getBean("uu",com.feng.entity.User.class);
System.out.println(user.getStr());
user.getCat().shout();
user.getDog().shout();
}
打印输出:
xiaoming
miao~
wang~
Spring 自动装配 ‘byType’
autowire=“byType”
这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
进行测试:
利用上面的案例,将xml文件中改为
<bean id="uu" class="com.feng.entity.User" autowire="byType">
<property name="str" value="xiaoming"></property>
</bean>
<bean id="dog" class="com.feng.entity.Dog"></bean>
<bean id="cat" class="com.feng.entity.Cat"></bean>
测试程序不变:
输出与上面结果一致
基于注解的配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。
在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。
注解连线在默认情况下在 Spring 容器中不打开。
所以做准备工作:
需要在我们的 Spring 配置文件中启用它
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--开启属性注解-->
<context:annotation-config/>
</beans>
看看几种注解:
[@Resource] [@Autowired] [@Qualifier]
在测试中认识一下,继续上面的栗子:
[@Autowired]
@Autowired是按类型自动转配的,不支持id匹配。将User中的set方法去掉,加入@Autowired注解。
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
//@Autowired(required=false)
false,对象可以为null;true,对象必须存对象,不能为null。
private String str;
// 开启属性注解后,去掉set方法,加入@Autowired注解
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public void setStr(String str) {
this.str = str;
}
}
xml中:
<bean id="catXX" class="com.feng.entity.Cat"></bean>
<bean id="dogX" class="com.feng.entity.Dog"></bean>
<bean id="uu" class="com.feng.entity.User"></bean>
结果输出正常,可发现catXX和dogX的id与类中不一致也不会影响结果
[@Resource]
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。
public class User {
@Resource
private Cat cat;
@Resource(name="dogggg2")
private Dog dog;
private String str;
....
}
xml文件中
<bean id="catXX" class="com.feng.entity.Cat"></bean>
<bean id="dogggg2" class="com.feng.entity.Dog"></bean>
<bean id="uu" class="com.feng.entity.User"></bean>
<!--测试后正常输出,虽没找到catXX,但是依据类型找到了-->
<!--不改变catXX,再把dogggg2改成其他之后,报错,因为@Resource(name="dogggg2")
没有对应的dogggg2,报错为NoSuchBeanDefinitionException: No bean named 'dogggg2' available
-->
@Resource 和@ Autowired 的区别:
- @ Autowired 通过byType的方式实现,而且必须要求这个对象存在!如果要允许null 值,可以设置它的required属性为false 【常用】
- @ Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!如果name属性一旦指定,就只会按照名称进行装配。 【常用】
- 执行顺序不同:@ Autowired 通过byType的方式实现。@ Resource 默认通过byname的方式实现。
8 使用注解开发
1 必须有AOP的包(添加了Spring-webmvc的jar包就会有)
2 导入context约束文件(与基于注解的配置开头处的xml文件一致)
测试注入Bean
在User类中
@Component("user1")
public class User {
private Cat cat;
private Dog dog;
private String str;
public void setCat(Cat cat) {
this.cat = cat;
}
public void setDog(Dog dog) {
this.dog = dog;
}
//相当于 <property name="str" value="小明明"/>
@Value("小明明")
public void setStr(String str) {
this.str = str;
}
...
}
xml文件中
<bean id="user1" class="com.feng.entity.User">
<property name="cat" ref="cat"></property>
<property name="dog" ref="dog"></property>
</bean>
<bean id="dog" class="com.feng.entity.Dog"></bean>
<bean id="cat" class="com.feng.entity.Cat"></bean>
进行测试打印:小明明 miao~ wang~
3 衍生的注解
@Component 有几个衍生注解,在web开发中,会按照mvc三层架构分层!
-
dao 【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
注解和XML的最佳使用
- xml管理Bean
- 通过注解完成属性的注入
使用Java的方式配置Spring
现在要完全不使用Spring的xml配置了,全权交给Java来做
使用一个User类
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
//@Value("英子")也可以
private String name;
public String getName() {
return name;
}
@Value("英子") //属性注入值
public void setName(String name) {
this.name = name;
}
}
此时不建立xml文件,使用一个类
// 这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
public class Myconfig {
//注册一个bean,就相当于之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user(){
return new User();
}
}
写测试类:
@Test
public void test6(){
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
User user = (User) context.getBean("user");
System.out.println(user.name);
}
,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
//@Value("英子")也可以
private String name;
public String getName() {
return name;
}
@Value("英子") //属性注入值
public void setName(String name) {
this.name = name;
}
}
此时不建立xml文件,使用一个类
```java
// 这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
public class Myconfig {
//注册一个bean,就相当于之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user(){
return new User();
}
}
写测试类:
@Test
public void test6(){
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
User user = (User) context.getBean("user");
System.out.println(user.name);
}
输出:英子
再如:建立一个Student类,使用Java 的方式配置Spring,类里面包含list,map,数组等属性
@Component
public class Student {
@Value("英子")
public String name;
@Autowired
public Address address;
@Resource
public String[] books;
@Resource
private List<String> list;
@Resource
private Map<String,String> map;
@Resource
private Set<String> set;
@Value("null")
private String wife;
private Properties info;
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public String[] getBooks() {
return books;
}
public List<String> getList() {
return list;
}
public Map<String, String> getMap() {
return map;
}
public Set<String> getSet() {
return set;
}
public String getWife() {
return wife;
}
public Properties getInfo() {
return info;
}
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.getAddress() +
", books=" + Arrays.toString(books) +
", list=" + list +
", map=" + map +
", set=" + set +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
建立一个配置类
@Configuration
public class StudentConfig {
@Bean
public Student student(){
return new Student();
}
@Bean
public Address address(){
return new Address();
}
@Bean
public String[] books(){
return new String[]{};
}
@Bean
public List<String> list() {
return new ArrayList<String>();
}
@Bean
public Map<String,String> map(){
return new HashMap<String, String>(){};
}
@Bean
public Set<String> set(){
return new HashSet<String>();
}
}
在测试:
@Test
public void test7(){
ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
Student student = (Student) context.getBean("student");
student.address.setAddress("西安");
String[] strings = new String[3];
strings[0]="三国";
strings[1]="水浒";
strings[2]="西游";
student.setBooks(strings);
ArrayList<String> strings1 = new ArrayList<String>();
strings1.add("清单1");
strings1.add("清单2");
student.setList(strings1);
HashMap<String, String> map = new HashMap<String, String>();
map.put("key1","Value1");
map.put("key2","Value2");
student.setMap(map);
HashSet<String> set = new HashSet<String>();
set.add("s1");
set.add("s2");
student.setSet(set);
System.out.println(student.toString());
}
输出打印:
Student{name='英子', address=西安, books=[三国, 水浒, 西游], list=[清单1, 清单2], map={key1=Value1, key2=Value2}, set=[s1, s2], wife='null', info=null}
这种纯Java的配置方式,在SpringBoot中就很常见了