从0到1学习Spring框架2

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 从0到1学习Spring框架

5.SpringIOC自动注入

5.1.Spring容器管理注解

Spring自动扫描,会把以下注解的bean纳入Spring容器管理

@Component不好分层时用该注解

@Controller控制层使用该注解

@Service业务层使用该注解

@Repository dao层使用该注解

5.2.AutoWired注入

  • 添加context命名空间,1拖2
<?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
        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>
  • applicationContext.xml文件配置扫描基本包
<context:component-scan base-package="com.tjetc"></context:component-scan>
  • @AutoWired自动注入
@Repository
public class UserDao {
    public void login(){
        System.out.println("用户登录");
    }
}
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    public void login(){
        userDao.login();
    }
}
ClassPathXmlApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = context.getBean(UserService.class);
bean.login();

运行结果

用户登录

5.3.接口多个实现类的注入方式

  • 准备UserDao接口,和两个实现类
public interface UserDao {
    void login();
}
@Repository
public class UserMySqlDaoImpl implements UserDao {
    public void login() {
        System.out.println("UserMySqlDaoImpl.login()");
    }
}
@Repository
public class UserOracleDaoImpl implements UserDao {
    public void login() {
        System.out.println("UserOracleDaoImpl.login()");
    }
}
  • 注意在UserService注入的时候,要在@Autowired下写@Qualifer(“bean的名字”)注解
@Service
public class UserService {
    @Autowired
    @Qualifier("userMySqlDaoImpl") //指定是哪个实现类
    private UserDao userDao;
    public void login(){
        userDao.login();
    }
}
  • 测试代码
ClassPathXmlApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = context.getBean(UserService.class);
bean.login();
  • 运行结果

UserMySqlDaoImpl.login()

5.4.Spring的5种自动装配模式

no:默认情况,不自动装配,手动设置bean

byName:根据bean 的名字自动装配

byType:根据bean的数据类型自动装配

constructor:根据构造函数的参数的数据类型,进行byType模式的自动装配

autodetect:如果发现默认的构造函数,用constructor模式,否则,用byType模式

  • 准备实体类Person、Customer
public class Customer {
    private Person person;
    public Customer(Person person) {
        System.out.println("调用构造注入");
        this.person = person;
    }
    public Customer() {
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
    @Override
    public String toString() {
        return "Customer{" +
                "person=" + person +
                '}';
    }
}
public class Person {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
  • 配置applicationContext.xml

no方式

<bean id="person" class = "com.tjetc.domain.Person">
   <property name="name" value="李祥"></property>
</bean>
<bean id="customer" class="com.tjetc.domain.Customer" autowire="no">
   <property name="person" ref="person"></property>
</bean>

byName方式

<bean id="person" class = "com.tjetc.domain.Person">
   <property name="name" value="李祥"></property>
</bean>
<bean id="customer" class="com.tjetc.domain.Customer" autowire="byType">
</bean>

byType方式

<bean id="person" class = "com.tjetc.domain.Person">
   <property name="name" value="李祥"></property>
</bean>
<bean id="customer" class="com.tjetc.domain.Customer" autowire="byType">
</bean>

constructor方式

<bean id="person" class = "com.tjetc.domain.Person">
   <property name="name" value="李祥"></property>
</bean>
<bean id="customer" class="com.tjetc.domain.Customer" autowire="constructor">
</bean>

5.5.注入方式总结

51f08fa0dd06441fba8336da55cbc635.png

6.Spring Bean的管理

6.1Bean的scope属性

(1)singleton单例(默认)

(1)singleton单例(默认)

当scope的取值为singleton时 Bean的实例化个数:1个 Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

Bean的生命周期:

对象创建:当应用加载,创建容器时,对象就被创建了

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”);

对象运行:只要容器在,对象一直活着

对象销毁:当应用卸载,销毁容器时,对象就被销毁

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton">
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao1 = (UserDao) app.getBean("userDao");
        UserDao userDao2 = (UserDao) app.getBean("userDao");
        System.out.println(userDao1);
        System.out.println(userDao2);

打印的userDao1、userDao2地址相同

(2)prototype多例

Bean的实例化个数:多个 Bean的实例化时机:当调用getBean()方法时实例化Bean

对象创建:当使用对象时,创建新的对象实例

UserDao userDao1 = (UserDao) app.getBean(“userDao”);

对象运行:只要对象在使用中,就一直活着

对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="prototype">
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao1 = (UserDao) app.getBean("userDao");
    UserDao userDao2 = (UserDao) app.getBean("userDao");
    System.out.println(userDao1);
    System.out.println(userDao2);

打印的userDao1、userDao2地址不同

(3)request ,session和global session

这三个类型是spring2.0之后新增的,他们不像singleton和prototype那么通用,因为他们只适用于web程序,通常是和XmlWebApplicationContext共同使用


<bean id ="requestPrecessor" class="...RequestPrecessor"   scope="request" />

Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,,该对象的生命周期即告结束。当同时有10个HTTP请求进来 的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上 说,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。

<bean id ="userPreferences" class="...UserPreferences"   scope="session" />

Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间,其他的方面真是没什么区别。

<bean id ="userPreferences" class="...UserPreferences"   scope="globalsession" />

global session只有应用在基于porlet的web应用程序中才有意义,他映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待。

6.2.Spring bean生命周期回调

(1)xml文件配置方式

  • 准备一个被spring容器管理的类
public class A {
    public A(){
        System.out.println("A()...");
    }
    public void init(){
        System.out.println("A.init()...");
    }
    public void destroy(){
        System.out.println("A.destroy()...");
    }
}
  • 在配置文件里配置bean、初始化方法、销毁方法
<bean id="a" class="com.tjetc.domain.A" init-method="init" destroy-method="destroy"></bean>
  • 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A bean = context.getBean(A.class);
System.out.println(bean);
context.close();
  • 运行结果

A()... A.init()... com.tjetc.domain.A@66a3ffec A.destroy()...

(2)JSR250注解@PostConstruct@PreDestroy

  • spring容器管理的类加上@Component,在初始化方法上加上@PostConstruct注解,在销毁方法上加上@PreDestroy注解
@Component
public class A {
    public A(){
        System.out.println("A()...");
    }
    @PostConstruct
    public void init(){
        System.out.println("A.init()...");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("A.destroy()...");
    }
}
  • applicationContext.xml
  <context:component-scan base-package="com.tjetc"></context:component-scan>
  • 测试代码不变
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A bean = context.getBean(A.class);
System.out.println(bean);
context.close();
  • 运行结果

A()... A.init()... com.tjetc.domain.A@44a3ec6b A.destroy()...

(3)接口InitializingBean和DisposableBean

  • spring容器管理的类要实现InitializingBean和DisposableBean接口
@Component
public class A implements InitializingBean, DisposableBean {
    public A(){
        System.out.println("A()...");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("A.afterPropertiesSet()...");
    }
    public void destroy() throws Exception {
        System.out.println("A.destroy()...");
    }
}
  • applicationContext.xml配置基本扫描包
<context:component-scan base-package="com.tjetc"></context:component-scan>
  • 测试代码不变
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A bean = context.getBean(A.class);
System.out.println(bean);
context.close();
  • 运行结果

A()... A.afterPropertiesSet()... com.tjetc.domain.A@4516af24 A.destroy()...

6.3.钩子关闭IOC容器

  • 使用钩子,在非WEB环境下,可以优雅的关闭IOC容器。
  • 如富客户端的桌面环境,可以向JVM注册一个钩子。即使程序非正常退出,钩子函数也会被执行,这样在钩子函数中做环境清理工作,如关闭非托管资源,就是非常有效的方法。

注册一个shutdown hook,需要调用ConfigurableApplicationContext接口中的registerShutdownHook()方法。

 @Test
  public void testHook() throws Exception {
    Runtime.getRuntime().addShutdownHook(new Thread(){//2.
      @Override
      public void run() {
        System.out.println("钩子函数清除垃圾");
      }
    });
    ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
    context.registerShutdownHook();//1.在容器关闭时使用上面的钩子函数释放资源
    context.close();
    System.out.println("我要正常关闭了");
  }

6.4.BeanPostProcessor接口

bean 的后置处理接口

IOC容器生成bean对象后,在bean初始化前后,你可以通过BeanPostProcessor接口定制你的业务逻辑,如日志跟踪等。

配置BeanPostProcessor后Bean的使用过程如下:

  • 写一个类实现InitializingBean, DisposableBean接口
@Component
public class A implements InitializingBean, DisposableBean {
    public A(){
        System.out.println("A()...");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("A.afterPropertiesSet()...");
    }
    public void destroy() throws Exception {
        System.out.println("A.destroy()...");
    }
}
  • 写一个类LogBean实现BeanPostProcessor接口,重写2个方法
@Component
public class LogBean implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("LogBean.postProcessBeforeInitialization():"+bean+":"+beanName);
        return null;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("LogBean.postProcessAfterInitialization():"+bean+":"+beanName);
        return null;
    }
}
  • 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A bean = context.getBean(A.class);
System.out.println(bean);
context.close();

运行结果

A()... LogBean.postProcessBeforeInitialization():com.tjetc.domain.A@10dba097:a A.afterPropertiesSet()... LogBean.postProcessAfterInitialization():com.tjetc.domain.A@10dba097:a com.tjetc.domain.A@10dba097 A.destroy()...

6.5.FactoryBean接口

  • FactoryBean就是对一个复杂Bean的包装,可以在FactoryBean中进行初始化,然后把初始化的值传给它包装的对象。
  • FactoryBean接口在Spring framework框架自身,有大量的实现,如用于创建动态代理对象的ProxyFactoryBean。
  • 实现FactoryBean中的getObject()方法,返回真正需要的对象。
  • 首先准备一个类,用于存放数据库连接属性
public class JDBCTest {
    private String url;
    private String username;
    private String password;
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
  • 加入maven依赖,mysql,spring-jdbc
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

配置数据源和基本扫描包在applicationContext.xml

<context:component-scan base-package="com.tjetc"></context:component-scan>
<bean id = "dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql:///test"></property>
    <property name="username" value="root"></property>
    <property name = "password" value="123456"></property>
</bean>
  • 写一个工厂类实现FactoryBean,InitializingBean接口,在初始化中完成属性的赋值
@Component
public class MyFactoryBean implements FactoryBean<JDBCTest>, InitializingBean {
    @Autowired
    private DriverManagerDataSource dataSource;
    private JDBCTest jdbcTest;
    public JDBCTest getObject() throws Exception {
        return jdbcTest;
    }
    public Class<?> getObjectType() {
        return JDBCTest.class;
    }
    public boolean isSingleton() {
        return true;
    }
    public void afterPropertiesSet() throws Exception {
        Connection connection = dataSource.getConnection();
        System.out.println("链接对象为:"+connection);
        jdbcTest = new JDBCTest();
        jdbcTest.setUrl(dataSource.getUrl());
        jdbcTest.setUsername(dataSource.getUsername());
        jdbcTest.setPassword(dataSource.getPassword());
    }
}
  • 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
JDBCTest jdbcTest = (JDBCTest) context.getBean("myFactoryBean");
System.out.println(jdbcTest.getUrl());
System.out.println(jdbcTest.getUsername());
System.out.println(jdbcTest.getPassword());
  • 运行结果

链接对象为:com.mysql.jdbc.JDBC4Connection@4b168fa9 jdbc:mysql:///test root 123456

6.6.JSR注解

(1)Spring与JSR330对应注解

Spring javax.inject.*
@Autowrid @Inject
@Component @Named/@ManagedBean
@Scope(“singleton”) @Singleton
@Qualifier @Qualifier/@Named
@Value
@Required
@Lazy
ObjectFactory Provider

(2)@Inject 是JSR 330的注解,在使用@Autowired地方,可以使用 @Inject代替

@Service
public class UserService {
    @Inject
    private UserDao userDao;
    public void login(){
        userDao.login();
    }
}

(3)@Named或者@ManagedBean,代替Component

@Named  //@ManagedBean(需要javaee7.0 maven依赖)
public class MyFactoryBean implements FactoryBean<JDBCTest>, InitializingBean {
    @Autowired
    private DriverManagerDataSource dataSource;
    private JDBCTest jdbcTest;
    public JDBCTest getObject() throws Exception {
        return jdbcTest;
    }
    public Class<?> getObjectType() {
        return JDBCTest.class;
    }
    public boolean isSingleton() {
        return true;
    }
    public void afterPropertiesSet() throws Exception {
        Connection connection = dataSource.getConnection();
        System.out.println("链接对象为:"+connection);
        jdbcTest = new JDBCTest();
        jdbcTest.setUrl(dataSource.getUrl());
        jdbcTest.setUsername(dataSource.getUsername());
        jdbcTest.setPassword(dataSource.getPassword());
    }
}

(4)@Resource代替@Inject、@Autowired

@Resource可以应用在属性、Set方法上,注入数据。

使用@Resource代替@Inject、@Autowired

与@Autowired相反,@Resource默认的装配方式是byName

如果不写name属性,用@Resource按名称装配,如果找不到就回退到按类型装配

如果写name属性,用@Resource(name=“userDaoMysql”)按名称装配,如果找不到就不能回退到按类型装配了

@Service
public class UserService {
    @Resource
    private UserDao userDao;
    public void login(){
        userDao.login();
    }
}

(5)@Required

必须输入,只能用在属性setter,配置文件配置,不推荐使用

(6)@Configuration @Bean

Configuration //配置类,相当于applicationContext.xml

@Bean //

@Configuration
public class MyConfig {
    @Bean
    public UserDao userDao(){
        return new UserMySqlDaoImpl();
    }
}

(7)@Primary

多个相同对象使用@Primary优先采用哪一个对象

@Repository
@Primary
public class UserOracleDao implements UserDao {
  @Override
  public void login() {
    System.out.println("UserOracleDao.login()...");
  }
}


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
Java 数据安全/隐私保护 Spring
Java 中 Spring Boot 框架下的 Email 开发
Java 中 Spring Boot 框架下的 Email 开发
249 2
|
6天前
|
缓存 前端开发 Java
【框架】Spring 框架重点解析
【框架】Spring 框架重点解析
21 0
|
6天前
|
XML Java 数据格式
Spring框架入门:IoC与DI
【5月更文挑战第15天】本文介绍了Spring框架的核心特性——IoC(控制反转)和DI(依赖注入)。IoC通过将对象的创建和依赖关系管理交给容器,实现解耦。DI作为IoC的实现方式,允许外部注入依赖对象。文章讨论了过度依赖容器、配置复杂度等常见问题,并提出通过合理划分配置、使用注解简化管理等解决策略。同时,提醒开发者注意过度依赖注入和循环依赖,建议适度使用构造器注入和避免循环引用。通过代码示例展示了注解实现DI和配置类的使用。掌握IoC和DI能提升应用的灵活性和可维护性,实践中的反思和优化至关重要。
22 4
|
6天前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
10 1
|
6天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
12 0
|
6天前
|
JSON 前端开发 Java
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解(下)
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解
7 0
|
6天前
|
JSON 前端开发 Java
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解(上)
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解
7 0
|
6天前
|
XML Java 应用服务中间件
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
10 0
|
6天前
|
Java 开发工具 Maven
根据SpringBoot Guides完成进行示例学习(详细步骤)
根据SpringBoot Guides完成进行示例学习(详细步骤)
9 1
|
6天前
|
XML Java 数据库连接
Spring框架与Spring Boot的区别和联系
Spring框架与Spring Boot的区别和联系
25 0