spring

寻技术 JAVA编程 / 其他编程 2023年07月11日 112

Spring

Spring简介

Spring 是一个开源的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应

特点:

  • Spring 是一个开源免费的框架,容器;
  • Spring 是一个轻量级的框架,非入侵式的;
  • 控制反转 IoC,面向切面编程 AOP;
  • 对事务的支持,对框架的支持。

优点:

  • 方便解耦,简化开发
  • AOP编程的支持
  • 声明式事务的支持
  • 方便程序的测试
  • 方便集成各种优秀的框架
  • 降低JavaEE API的使用难度

Spring的体系结构:

Spring入门

Spring的开发步骤示意图:

Spring程序开发步骤

  1. 在maven导入Spring开发的基本包坐标

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.6</version>
        </dependency>
    </dependencies>
    
  2. 编写Dao接口和实现类

  3. 创建Spring核心配置文件

  4. 在Spring配置文件中配置UserDaoImpl

  5. 使用Spring的API获得Bean实例

    public class UserDaoDemo {
        public static void main(String[] args) {
    //        参数为spring配置文件
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            //参数为配置文件中为该实现类配置的id
            UserDao userDao =(UserDao) app.getBean("userDao");
            userDao.save();
        }
    }
    

​ 成功运行

之前运行提示:Error:java: 错误: 不支持发行版本 5,在pom.xml中添加该配置,11为我的java版本

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

Spring配置文件

Bean标签

  • 用于配置对象交由spring创建,默认情况下调用类的无参构造函数,不是无参构造函数不能创建成功

  • 基本属性:id:唯一标识

    ​ class:全限定名称

    <bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl"></bean>
    
  • 范围配置:scope:每次通过spring得到的bean实例对象是不同的实例对象或者是同一个(单例)

    与此同时单例在就加载配置文件时就进入了无参构造,只要容器在对象一直存在;而多例在获得bean实例对象是进入无参构造,只要对象在使用一直存在,长时间不用被回收

  • Bean生命周期配置:

    • init-method:指定类中的初始化方法名称

      指定该类中的save方法在初始化时调用,销毁同理:
      <bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl" scope="prototype" init-method="save"></bean>
      
    • destroy-method:指定类中销毁方法名称

  • Bean实例化三种方式:

    • 无参构造方法实例化:默认,见上

    • 工厂静态方法实例化:

      StaticFactory是工厂,getUserDao是StaticFactory的方法
      <bean id="userDao" class="com.myspring.dao.impl.StaticFactory" factory-method="getUserDao"></bean>
      
      package com.myspring.dao.impl;
      
      public class StaticFactory {
          public  static  UserDaoImpl getUserDao(){
              return  new UserDaoImpl();
          }
      }
      
    • 工厂实例方法实例化

    <bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl"></bean>
    因为非static需要实例对象,所以创建
    <bean id="factory" class="com.myspring.dao.impl.Factory"></bean>
    再讲两种联系起来
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>
    
    package com.myspring.dao.impl;
    
    public class Factory {
        public   UserDaoImpl getUserDao(){
            return  new UserDaoImpl();
        }
    }
    

依赖注入

  • 依赖注入是spring框架核心ioc的具体实现

  • 在编写程序时,层与层(例如业务层调用持久层方法)之间的依赖关系可以通过调用sprin来维护,简单来说就是让框架把持久层对象传入业务层,不用我们直接获取

两种方式:

  1. set方法:我的理解是相当于通过spring调用它的set方法

    //业务层中
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        public  void save(){
            userDao.save();
        }
    }
    //外部层:
    public class UserDaoDemo {
        public static void main(String[] args) {
    //        参数为spring配置文件
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            //参数为配置文件中为该实现类配置的id
            UserService userService =(UserService) app.getBean("userService");
            userService.save();
        }
    }
    //dao层代码和上面的一样
    
    spring配置文件中
    <bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl"></bean>
        <bean id="userService" class="com.myspring.service.impl.UserServiceImpl">
    <!--        name参数是userService中set方法名字set后面的单词首小写,ref:表示引向的bean实例id-->
            <property name="userDao" ref="userDao"></property>
        </bean>
    

​ 简便方法:p命名空间方式(本质上还是set):

​ 使用方法:修改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"
<!--   添加这一句    -->
     xmlns:p="http://www.springframework.org/schema/p">
<bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl"></bean>
<!--  bean修改成这样 -->
    <bean id="userService" class="com.myspring.service.impl.UserServiceImpl" p:userDao-ref="userDao">
    </bean>
</beans>
  1. 构造方法

​ 同理把业务层的set方法创建改成有参方法创建

在spring配置文件中:

   <bean id="userService" class="com.myspring.service.impl.UserServiceImpl">
<!-- name:构造方法参数名字,ref:bean id-->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

其他数据注入

当然,除了可以注入bean实例,还可以注入其他数据:

  • 普通数据类型
  • 引用数据类型
  • 集合数据类型
本质还是set方法
    <bean id="userService" class="com.myspring.service.impl.UserServiceImpl">
<!--        基本数据类型-->
        <property name="age" value="1"></property>
<!--        引用数据类型-->
        <property name="name" value="lihua"></property>
<!--        list-->
        <property name="friends">
            <list>
                <value>xiaoming</value>
                <value>xiahu</value>
            </list>
        </property>
<!--        map-->
        <property name="familyName">
            <map>
                <entry key="mother" value="lili">
                </entry>
                <entry key="father" value="ligang">
                </entry>
            </map>
        </property>
<!--        props-->
        <property name="properties">
            <props>
                <prop key="p1">aaa</prop>
                <prop key="p2">bbb</prop>
            </props>
        </property>
    </bean>

引入其他配置文件

当spring配置文件过大时,可以根据不同功能拆分成几个配置文件

那么只需要在主文件中引入拆分的配置文件:

<import resource="applicationContext.xml"></import>

Spring相关API

ApplicationContext:接口类型,代表引用上下文,可以通过其实例获得Spring容器中的Bean对象

其体系结构:

ApplicationContext的实现类

  1. ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件推荐使用(resources目录下)文件类型xml
  2. FileSystemXmlApplicationContext:磁盘任意路径加载配置文件文件类型xml
  3. AnnotationConfigApplicationContext:使用注解配置容器对象,需要此类来创建spring容器。它用来读取注解

getBean()方法的使用

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过bean实例的id得到
UserService userService =(UserService) 
    app.getBean("userService");
//通过class类型寻找得到
app.getBean(UserService.class);

Spring配置数据源

数据源(连接池)

数据源的作用

常见数据源:DBCP、C3P0、BoneCP、Druid等

创建数据源

数据源的开发步骤:

  1. 导入数据源坐标和数据库驱动坐标(数据源可以自己选择导入那个公司的)
<!--            mysql-->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>'
<!--        数据源一:c3p0-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
<!--        数据源二:druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
<!--    junit:单元测试框架,与数据源没联系单纯方便之后测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
  1. 创建数据源对象(c3p0),其他数据源的创建可以自行搜索

  2. 设置数据源的基本连接数据

  3. 使用数据源获取连接资源和归还资源

    public class DataSourceTest {
    //    这就是junit的
        @Test
        //测试手动创建c3p0数据源
        public  void test1() throws PropertyVetoException, SQLException {
    //        创建数据源:这是c3p0的创建方法
            ComboPooledDataSource dataSource=new ComboPooledDataSource();
    //        配置基本参数:一数据库驱动
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
    //        数据库url
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/school");
    //        用户名
            dataSource.setUser("root");
    //        密码
            dataSource.setPassword("123456");
    //        获取资源
            Connection connection = dataSource.getConnection();
    //        测试connection是否为空
            System.out.println(connection);
            connection.close();
        }
    }
    

    运行测试结果:不为空所以成功

    当然,为了降低耦合最好还是把数据库相关信息抽取出来为一个单独的配置文件

    改进之后:

    public class DataSourceTest {
    //    这就是junit的
        @Test
        //测试手动创建c3p0数据源
        public  void test1() throws PropertyVetoException, SQLException {
            //读取配置文件;参数是相对于resources目录的路径,且不需要写properties后缀
            ResourceBundle rb = ResourceBundle.getBundle("jdbc");
            String driver = rb.getString("jdbc.driver");
            String url = rb.getString("jdbc.url");
            String username = rb.getString("jdbc.username");
            String password = rb.getString("jdbc.password");
            //创建数据源对象、设置连接参数
            //        创建数据源:这是c3p0的创建方法
            ComboPooledDataSource dataSource=new ComboPooledDataSource();
    //        配置基本参数:一数据库驱动
            dataSource.setDriverClass(driver);
    //        数据库url
            dataSource.setJdbcUrl(url);
    //        用户名
            dataSource.setUser(username);
    //        密码
            dataSource.setPassword(password);
    //        获取资源
            Connection connection = dataSource.getConnection();
    //        测试connection是否为空
            System.out.println(connection);
            connection.close();
        }
    }
    

Spring 配置数据源

刚才是通过new来创建数据源,现在我们将数据源的创建权交给Spring容器去完成

配置文件中:

<!--   可以通过右键数据源对象copy reference获得class路径-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 为dataSource配置属性,name中是固定写法,都是数据源自己的属性-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/school"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

外部类中:

@Test
//测试手动创建c3p0数据源
public  void test1() throws PropertyVetoException, SQLException {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    DataSource dataSource = app.getBean(DataSource.class);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();

}

如果要在spring配置文件中引入properties文件

<!--    加载外部properties文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--   可以通过右键数据源对象copy reference获得class路径-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--        不用再单独来个配置文件通过这样的方式配置,name为数据源的属性-->
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

Spring的注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所有注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率

Spring原始注解

  • 主要替代配置

  • 原始注解有:

    注解 说明
    @Component 使用在类上用于实例化Bean
    @Controller 使用在web层类上用于实例化Bean
    @Service 使用在service层类上用于实例化Bean
    @Repository 使用在dao层类上用于实例化Bean
    @Autowired 使用在字段上用于根据类型依赖注入
    @Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入
    @Resource 相当于@Autowired+@Qualifier,按照名称进行注入
    @Value 注入普通属性
    @Scope 标注Bean的作用范围
    @PostConstruct 使用在方法上标注该方法是Bean的初始化方法
    @PreDestroy 使用在方法上标注该方法是Bean的销毁方法

    下面例子配置和注解等同:

    //<bean id="userDao" class="com.myspring.dao.impl.UserDaoImpl"></bean>
    @Component("userDao")
    public class UserDaoImpl implements UserDao{}
    
    //  注入bean实例  <property name="userDao" ref="userDao"></property>
    //另外xml文件配置的方式该属性必须有set或者构造函数,但是注解方法可以省略
        @Autowired//按照数据类型从spring容器中进行匹配
        @Qualifier("userDao")//按照id的值从容器中进行匹配的,但是此处结合 @Autowired和 @Qualifier一起使用
        private  UserDao userDao;
    

    最后,我们需要再配置文件中配置组件扫描:以识别注解

    <!--配置组件扫描:base-package扫描包下所有-->
        <context:component-scan base-package="com.myspring"></context:component-scan>
    

    其他原始注解讲解:

    • @Controller、@Service、@Repository这三者和@Component作用完全相同但是,分别表示web层,service层、dao层的bean实例

    • @Resource(name="userDao")就等于@Autowired+@Qualifier("userDao")

    • @value:

      //<property name="age" value="12"></property>
      @Value("12")
      private  int age;
      //这个东西需要在配置文件中已载入properties文件,具体见上
        @Value("${jdbc.driver}")
       private  String name;
      
    • @Scope("singleton"),参考bean的scope属性

    • @PostConstruct完全类似bean的init-method

    • @PreDestroy类似bean的destroy-method

新注解

除了原始注解外,我没还需要其他注解来覆盖其他原始注解无法实现的功能,例如:

  • 非自定义的bean
  • 加载properties文件配置
  • 组件扫描
  • 引入其他文件

新注解包括:

注解 说明
@Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定 Spring 在初始化容器时要扫描的包。作用和在Spring的xml配置文件中的<context:component-scan base-package="com"/>一样
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource 用于加载.properties 文件中的配置
@Import 用于导入其他配置类

新注解详解实例:

datasourceconfiguration配置类:

//加载properties配置文件
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
    //    通过这种方法赋值
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")//spring会将当前方法的返回值以指定名称存储到spring容器中,参数为id
    public DataSource getDataSource() throws PropertyVetoException {
        //        创建数据源:这是c3p0的创建方法
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
//        配置基本参数:一数据库驱动
        dataSource.setDriverClass(driver);
//        数据库url
        dataSource.setJdbcUrl(url);
//        用户名
        dataSource.setUser(username);
//        密码
        dataSource.setPassword(password);
        return  dataSource;
    }
}
/*相当于xml配置文件中的:
<!--    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">-->
<!--        <property name="driverClass" value="${jdbc.driver}"></property>-->
<!--        <property name="jdbcUrl" value="${jdbc.url}"></property>-->
<!--        <property name="user" value="${jdbc.username}"></property>-->
<!--        <property name="password" value="${jdbc.password}"></property>-->
<!--    </bean>-->

核心配置类:

//标志该类是Spring的核心配置
@Configuration
//扫描com下的所有,相当于<context:component-scan base-package="com.myspring"></context:component-scan>
@ComponentScan("com.myspring")
//导入datasourceconfiguration配置类,该类专注于数据库相关
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {

}

外部类测试方法中:

//        配置文件为类时加载,区别于xml
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        DataSource dataSource = app.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();

运行成功。

Spring集成Junit

spring测试还是比较麻烦,可以通过Junit来简化

  • 让SpringJunit来复制spring容器的创建,但需要将配置文件的名称告诉他:可以简化这两句

        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        DataSource dataSource = app.getBean(DataSource.class);
    
  • 将需要bean自己在测试类中注入

集成Junit的步骤:

  1. 导入spring集成Junit的坐标

    junit的坐标

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    

    spring集成junit坐标:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.6</version>
    </dependency>
    
  2. 使用@Runwith注解替换原来的运行期

  3. 使用@ContextConfiguration指定配置文件或配置类

  4. 使用@Autowired注入需要测试的对象

  5. 创建测试方法进行测试

//java测试类中:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")//配置类参数写(classes={xxx.class})
public class SpringJunitTest {
   @Autowired
    private  UserService userService;

   @Test
   public  void test(){
       userService.save();
   }
}
关闭

用微信“扫一扫”