Spring的学习

89 阅读5分钟

前期配置

在setting配置中,对maven中的文件存放目录进行修改,然后创建maven的project即可

对IOCD理解

IOC:以前我要在java类中new,现在我把控制权交给Spring,他通过配置文件进行操作;

IOC:控制反转;

只要不是类和类间的连接性高,我们就可以称他为一个好的程序项目,Spring就是用来解决这个问题的

IOC制作步骤

1.导入spring坐标 5.1.9release、在pom.xml中

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>

2.编写业务层与表现层(模拟)接口与实现类(在main中的java文件里创建)

接口
public interface UserService {
    public void save();
}

实现类

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("user service running...");
    }
}

3.建立spring配置文件(在resource中创建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">

4.配置所需资源(Service)为spring控制的资源(创建bean标签,id=接口的小名,class=实现接口类的所在目录)

<!--  1.创建spring控制的资源,spring开始控制资源了
  id最好是接口名第一个单词小写,可以任意取;后面的是实现类-->
        <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>

5.表现层(App)通过spring获取资源(Service实例)(通过ApplicationContext获得xml文件,然后通过接口获取getBean里的id就是接口小名,然后再获取其中实现类的方法)

public class UserApp {
    public static void main(String[] args) {
    //2.加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    //3.获取资源,get自己定义的名字
        UserService userService = (UserService) ctx.getBean("userService");
        userService.save();
    }
}

对bean标签的深入探讨

除了id和class属性外,还有name属性,用来给bean起多个别名,id我觉得取的名字不好 在App表现层获取:接口UserService 对象名userService = (UserService)ctx.getBean("userService或者userService1...")

bean标签的scope属性

1.单例对象:默认;
<bean id="userService" scope="singleton" class="com.itheima.service.impl.UserServiceImpl"/>
2.非单例对象:
<bean id="userService" scope="prototype" class="com.itheima.service.impl.UserServiceImpl"/>

Bean的生命周期

init和destroy方法都是在Impl实现类中创立的,也就是后面的class
<bean id="userService" scope="singleton" init-method="init"方法名 destory-method="destory"方法名 class="com.itheima.service.impl.UserServiceImpl"/>

init和是否单例有关系,单例模式init只走一次,非单例模式:创建一次,我走一次

Bean的工厂模式

静态工厂模式

创建一个工厂类:UserServiceFactory

public class UserServiceFactory{
    public static UserService getService(){
        return new UserServiceImpl();
    }
}
<bean id="userService" class="com.itheima.service.UserServiceFactory" factory-method="getService"方法名/>
然后再去AplicationContext.xml文件中调用

实例工厂模式

public class UserServiceFactory2{
    这里没有staticpublic UserService getService(){
        return new UserServiceImpl();
    }
}
实例工厂对应的bean
<bean id="userService" class="com.itheima.service.UserServiceFactory2"/>

实例工厂创建bean,依赖工厂对象与对应的bean
<bean factory-bean="userService"和上方的id必须一致,问你工厂bean是哪一个 factory-method="getService"问你方法名>

DI:依赖注入,应用程序运行依赖的资源由Spring为其提供,资源进入应用程序的方法称为注入

set注入(主流)

创建DAO层 1.接口类

public interface UserDao(){
    public void save();
}

2.创建该接口类的实现类

public class UserDaoImpl UserDao(){
    public void save(){
        System.out.println("userDaoImpl running")
    }
}

3.在另一个实现类中使用

public calss UserServiceImpl UserService(){
    1.定义变量
    private UserDao userDao;
    2.创建set,对需要进行注入的变量添加set方法   
    public void setUserDao(UserDao userDao){
                    this.userDao = userDao;
        }
    public void save(){
        System.out.println("user service running...")
        最后这里就可以使用userDao了
        userDao.save();
    }
}

4.去配置文件中声明

将要注入的资源(userDao需要被注入)声明为bean
<bean id="userDao" class="com.itheima.dao.Impl.UserDaoImpl"/>

<bean id = "userService" class = "com.itheima.service.impl.UserServiceImpl">
    3.通过property属性进行注入
    <property name="userDao"set方法中后面那个名字的小写 ref="被注入的id号,也就是userDao"/>
</bean>

set注入,非引用类型

引入一个int型

private int num;
添加set方法
public void setNum(int num){
    this.num = num;
}

不用再写bean标签了,他不是个资源
直接写注入,在哪引入的,id和class就是哪里,这里我省略了
<property name="num" value="123"/>

然后再我引入num的那个实现类,也就是UserServiceImpl中就可以引用了num
Sout(num) //123

构造器注入(了解)

在UserServiceImpl中,我想使用UserDao层的数据

private UserDao userDao;
private int num;
public UserServiceImpl(UserDao userDao,int num...){
    this.userDao = userDao;
    this.num = num;
}
在ApplicationContext.xml中

将要注入的资源(userDao需要被注入)声明为bean
<bean id="userDao" class="com.itheima.dao.Impl.UserDaoImpl"/>
<bean id = "userService" class = "com.itheima.service.impl.UserServiceImpl">
    <constructor-arg name="userDao"ref="被注入的id号,也就是userDao"/>
    <constructor-arg name="num" value="123456"/>
    也可以在该标签里写 index,index的值就是传参的顺序
<bean/>

集合类型数据注入

在UserServiceImpl中调用BookDao中的数据

<--------------------------------->
private UserDao userdao
private BookDao bookdao
public void setBookDao(BookDao bookdao){
    this.bookdao = bookdao;
}
public void setUserDao(UserDao userdao){
    this.userdao = userdao;
}
<--------------------------------->
中间部分是我想调用这两个Dao,需要实现的写法

public void save(){
    调用bookDao中里的save方法
    bookDao.save();
}

在BookDaoImpl中

private ArrayList array;
public void setArray(ArrayList array){
    this.array = array
}

在ApplicationContext.xml中配置

<bean id = "userDao"...>

我需要UserService可以用到userDao和BookDao的,实现写法
<bean id = "userService接口小名" class = "com.itheima.service.impl.UserServiceImpl">
    <property name = "userDao是set方法中后面那个名字的小写" ref="对应被注入的的id号userDao"/>
    <property name = "bookDao" ref="bookDao"/>
    要想使用property,就得在此文件里写bean id对应的数据,如下:
</bean>

bookDao的set注入
<bean id = "bookDao"...>

    <property name="arraylist">
        <list>
            <value>666</value>
        </list>
    </property>
    
    <property name="properties">
        <list>
            <props>
                <prop key="属性名">value值</prop>
            </props>
        </list>
    </property>
    
    <property name="arraylist">
        <list>
            <value>666</value>
        </list>
    </property>
    
    <property name="array">
        <array>
            <value>666</value>
        </array>
    </property>
    
    <property name="hashset">
        <set>
            <value>666</value>
        </set>
    </property>
    
    <property name="hashset">
        <map>
            <entry key ="name" value="abcd"/>
        </map>
    </property>
    
<bean/>

使用p命名空间简化配置(了解)

1.需要在ApplicationContext.xml中 xmlns:xsi="..."的下面一行书写: xmlns:p="www.springframewrork.org/schema/p"

读取properties文件信息

1.在resources中创建 data.properties文件 书写 user:root pwd:123

2.在ApplicationContext.xml中配置 xmlns:context="www.springframework.org/schema/cont…"

  1. 加载配置文件 依然在xml中配置书写

    <context:property-placeholder location="classpath:*.properties"/>

        xsi:schemaLocation=
        "http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         
再多添加:"http://www.springframework.org/schema/context
          https://www.springframework.org/schema/context/spring-context.xsd     
         ">
         把所有的beans改成context
代码举例:
    在Impl中书写
        private String userName;
        private String password;
    然后创建set
        public void setUserName(String userName){
            this.userName=userName
        }
        
    在xml中
        <bean id="userDao" class="...impl">
            在这里使用data.properties文件中的数据
            <property name="set的首字母小写userName" value="${user }"/>
        </bean>

import导入配置文件

创建多个配置文件,将多个注入放在不同的文件里,最后import引入即可。

在主文件: <context:property-placeholder location="classpath:*.properties"/>

常用注解

启动注解功能 component

    <context:component-scan base-package="packageName"/>
        
     扫描itheima下面所有的包的注解,在ApplicationContext.xml中书写   
例如:<context:component-scan base-package="com.itheima">
    我想UserServiceImpl变成一个bean
    @component("userService")  指定名称(bean-id),然后就可以在App中用userService调用方法
    public class UserServiceImpl implements UserService{
        ...
    }
    
    之前是
    <bean id = "userService接口小名" class = "com.itheima.service.impl.UserServiceImpl">

@component衍生的注解

@component一样的作用,但是区分了对应层
    1.@Repository("userDao")  在Impl文件里用
    2.@Controller
    3.@Service
    4.@Component

bean的作用域

在类的上方书写 @Scope,用来区分单例和多例。默认:singleton

bean的生命周期

@PostConstruct@PreDestroy

bean的非引用类型属性注入

通过@Value,有了它,就可以省略set方法了,set方法诞生的目的就是为了给属性赋值
    在Impl中
        @Value("3")给num一个值为3
        private int num;
    
    @Value("abc")
        private String version;
    
    直接在该文件里使用
        sout(num,version)

bean的引用类型属性注入(省略set)

    在UserServiceImpl文件里
            @Autowired
            private UserDao userDao;
        
        在UserDaoImpl中要书写
            @Repository("userDao")
            public void save(){...}
        
        然后就可以直接在ServiceImpl里调用UserDaoImpl的数据
            userDao.save(); 
注:书写一个UserDao类型的数据时,用Autowired可以自动匹配上;如果是多个UserDao类型的就会报错,需要指定 
        通过@Qualifier("userDao1");就是@Repository("名字");
        名字要对应上;如果@Component没有写名字,默认用的就是所在类名
        
        如果不知道具体先运行哪个
            @Primary

加载properties文件中的属性

    1.创建jdbc.properties文件
        jdbc.userName=root123
        jdbc.passWord=itheima
        
    2.在需要使用的地方使用
        @PropertySource("classpath:jdbc.properties")
        public class BookImpl implements BookDao{
            @Value("${jdbc.userName}")
            private String userName;
        }
    3.直接疯狂输出
        sout(userName)

纯注解格式配置

 @Configuration:可以直接省略ApplicationContext.xml中的xml配置
 @ComponentScan("base-package所在位置"):
     我以前在里面配置过<context:component-scan base-package="..."/>
     这个是用来解决注解的,但是我现在使用@ComponentScan就可以代替他了
 

这个时候就不需要再创建ApplicationContext.xml文件了 我创建一个SpringConfig文件,但是在App也就是main中要修改代码:

    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = (UserService)ctx.getBean("userService");
    userService.save();

bean加载控制

Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中;

@DependsOn:控制bean的加载顺序,使指定的bean加载完毕在加载其他的 配置在方法上,@DependsOn指定的bean优先于@Bean配置的bean进行加载 配置在类上

@Order:配置类注解,控制配置类的加载顺序

@Lazy:延迟加载,放在bean定义的位置