【 Java之Spring框架】

Spring框架

在软件开过程中,我们需要使用到实例。我们又期望这些实例以单例的形式存在。现在我们要获得单例没有一个统一的入口,这时候就有了Spring。

我们使用Spring将这些实例统一的管理起来了,如果要获得某个类型的实例,可以通过Spring来获得

使用Spring容器来管理实例,并且从Spring容器中取出实例

组件:Spring容器管理的实例叫组件

注册:注册组件,Spring容器管理实例

引入依赖

Spring的依赖5+1

spring-core\context\aop\expression\beans + jcl(日志包)

junit

Spring依赖的特征:spring-xxx(官方)、 xxx-spring(非官方)

<!--5+1-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.15.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

配置文件

为啥要使用配置文件。使用配置文件将需要管理的实例做统一的管理

Spring配置文件是xml格式的 → 使用xml需要规范,xml中可以使用哪一些标签、可以使用哪一些属性,标签又有什么样的子标签 → 约束,同时也提供了解析的方式

我们需要去引入约束(头)

  • 已有的项目复制(×)

  • 文件模板(×)

  • Spring官方文档 →

    → schema约束

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!-- bean definitions here -->
    <bean id="userService" class="proxy.service.UserServiceImpl"/>
</beans>

注册组件

我们要使用Spring容器管理组件

我们需要有对应的类,才能管理它的实例。

<!--注册组件 → UserServiceImpl-->
<!--id属性:组件在容器中唯一标识-->
<!--class属性:全限定类名 ctrl + alt + shift + c-->
 <bean id="userService" class="proxy.service.UserServiceImpl"/>

测试

@Test
public void test1(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
            "application.xml");//初始化容器
    //获得对象,建议用接口接收,方便后期修改维护
    UserService  userService = (UserService) classPathXmlApplicationContext.getBean("userService");
    userService.say();
}

取出组件的方式

getBean

//getBean(String)  → 按照组件id取出组件

//getBean(Class)   → 按照组件类型取出组件(该类型的组件只有一个)
//applicationContext.getBean(UserService.class)

//getBean(String,Class)  → 按照组件id和类型    
@Test
public void mytest() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

    //按照组件id取出
    UserService userService1 = (UserService) applicationContext.getBean("userService");

    //按照类型取出
    UserService userService2 = applicationContext.getBean(UserService.class);

    //按照组件id和组件类型
    UserService userService3 = applicationContext.getBean("userService", UserService.class);

    userService1.sayHello("world");
}

最好使用id+组件类型共同配合,这样可以避免类型强转出现的问题

当使用按照类型取出的时候,如果相同类型的组件不止一个,那么就会出现问题

<bean id="userService" class="com.cskaoyan.service.UserServiceImpl"/>
<bean id="userService2" class="com.cskaoyan.service.UserServiceImpl2"/>

会出现以下的异常

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.cskaoyan.service.UserService’ available: expected single matching bean but found 2: userService,userService2

维护多个组件之间的关系

即,在一个组件中维护其他组件中的对象

<bean id="userService" class="com.cskaoyan.service.UserServiceImpl">
    <!--name属性:给成员赋值,使用的是set方法赋值的-->	
    <!--ref属性:引用了容器中的组件的id-->
    <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao" class="com.cskaoyan.dao.UserDaoImpl"/>

核心接口

容器接口:ApplicationContext、BeanFactory(ApplicationContext的父接口)

ApplicationContext:ClassPathXmlApplicationContext、AnnotationConfigApplicationContext、WebApplicationContext

Spring底层就是维护了一个HashMap

BeanFactory和FactoryBean之间的关系?

联系:都是工厂设计模式,都是向容器中注册组件

区别:BeanFactory注册的全部组件;FactoryBean注册的是特定的组件

  1. BeanFactory:负责生产和管理Bean的一个工厂接口,提供一个Spring Ioc容器规范,
  2. FactoryBean: 一种Bean创建的一种方式,对Bean的一种扩展。对于复杂的Bean对象初始化创建使用其可封装对象的创建细节。

Bean的实例化

构造方法

无参构造和有参构造皆可,默认使用的是无参构造,可以通过property来进行赋值

<!--默认使用的是无参构造-->
<bean id="noParamConstructorBean" class="com.cskaoyan.bean.NoParamConstructorBean">
    <property name="username" value="songge"/>
</bean>

如果要使用有参构造,那么需要在子标签中写入构造方法的形参名

<bean id="hasParamConstructorBean" class="com.cskaoyan.bean.HasParamConstructorBean">
    <!--name属性:有参构造方法的形参名-->
    <constructor-arg name="username" value="ligenli"/>
</bean>

工厂

静态工厂和实例工厂

静态工厂注册:

<bean id="carFromStaticFactory" class="com.cskaoyan.factory.CarStaticFactory" factory-method="create"/>

指明调用静态工厂中的方法名即可,会自动调用并产生实例,使用factory-method标签

实例工厂注册:首先要用无参构造完成工厂的实例化,然后再调用实例工厂的工厂方法

<!--工厂的实例化 → 无参构造先完成工厂的实例化-->
<bean id="instanceFactory" class="com.cskaoyan.factory.CarInstanceFactory"/>
<!--使用工厂实例调用生产方法:唯一一个没有使用class属性的bean标签-->
<bean id="carFromInstanceFactory" factory-bean="instanceFactory" factory-method="create"/>
***FactoryBean

将方法的返回值作为容器中的组件

工厂方法:定义工厂接口,来实现工厂接口

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    //getObject是其核心方法 → 返回值作为容器中的组件
    //类定义的泛型体现在返回值类型
    @Nullable
    T getObject() throws Exception;

    //组件类型
    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

在注册组件的时候有个命名的规则xxxFactoryBean,注册的组件就是xxx

/**
 * FactoryBean
 * XXXFactoryBean → 注册的组件就是XXX
 * @author stone
 * @date 2021/12/25 11:31
 */
public class CarFactoryBean implements FactoryBean<Car> {
    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        car.setSource("factory bean");
        return car;
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }	
}

组件注册

只需要将FactoryBean注册到容器中即可

<!--FactoryBean只需要注册到容器中-->
<bean id="carFromFactoryBean" class="com.cskaoyan.factory.CarFactoryBean"/>

FactoryBean是Spring框架中非常特殊的一个接口,我们注册组件的时候,我们写的是其其实现类,而最终取出的组件是其getObject方法的返回值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Srping容器在取出bean的时候,会先判断这个bean有没有实现FactoryBean,如果有就返回getObject中的返回值,如果没有就返回bean

Scope作用域

singleton 单例:每一次取出来是同一个

prototype 原型:每一次取出来是一个新的 → 每一次取出组件的时候才会开始生命周期

配置作用域

<bean id="singletonBean" class="com.cskaoyan.scope.SingletonBean" scope="singleton"/>
<bean id="prototypeBean" class="com.cskaoyan.scope.PrototypeBean" scope="prototype"/>
<bean id="defaultBean" class="com.cskaoyan.scope.DefaultBean"/>

默认的作用域是单例,绝大多数情况下我们使用的也是单例

生命周期

组件从实例化开始到组件可用状态这个时间经过哪些方法,在容器关闭的时候也会影响

Scope对生命周期的影响:

1. Prototype组件:每一次取出组件的时候才会开始生命周期 → 懒加载,容器关闭不会执行方法
2. Singleton组件:容器初始化的时候就已经开始生命周期 → 立即加载,容器关闭会执行方法

组件在容器中默认以单例的形式存在,单例组件在容器初始化时开始生命周期,生命周期的起点是Bean的实例化

Aware接口

如果你需要Aware提供的值,才去实现对应的接口才会执行到对应的set方法

使用的是set方法进行赋值,需要使用一个成员变量接收。

设置BeanName

public interface BeanNameAware extends Aware {
    void setBeanName(String var1);
}

设置BeanFactory

public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory var1) throws BeansException;
}

设置上下文

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext var1) throws BeansException;
}

***BeanPostProcessor接口

BeanPostProcessor接口提供的方法是给容器中的所有的组件使用的,相当于是一个通用性的方法,容器中的所有组件都会执行到BeanPostProcessor的postProcessBeforeInitialization和postProcessAfternitialization方法

对所有的组件进行通用性的处理,也可以根据传入的参数来进行判断,对某一类或者某一个组件进行单独的处理

生效条件:首先定义一个类实现BeanPostProcessor接口,并且将其注册为容器中的组件。

针对于BeanPostProcessor而言,作用范围是:除了BeanPostProcessor这个组件之外,其他的所有组件

public interface BeanPostProcessor {
    /**
    * bean: 正在执行生命周期的组件
    * beanName: 正在执行生命周期的组件id
    * 返回值:Object类型,需要注册到容器中的实例
    */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

针对于返回值,如果返回值为null那么就返回原先的bean,如果不是null,就返回自定义的值并注册到组件中

个性化处理:

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("组件" + beanName +"的BeanPostProcessor的before方法");
    //针对于容器中的某一类或某一个组件可以在这里做通用性的处理
    // 能否找到所有的DataSource组件做处理
    if (bean instanceof DataSource) {
        //个性化的处理
        // 数据库的密码是123456
        // 我这里可以获得配置文件中的abcdef
        // 把abcdef转换为你需要的123456
    }
    // 我能否找到lifeCycleBean这个组件
    if ("lifeCycleBean".equals(beanName)) {
        //原先的username是songge → 组件到达可用状态之前变成ligenli
        LifeCycleBean temp = (LifeCycleBean) bean;
        temp.setUsername("ligenli");
        return temp;
    }
    // 传入的是委托类的实例,返回的是代理对象
    return bean;
}

主要关注的是通用性,在通用性里也可以自己写代码完成个性化的处理

before和after之间的过程

InitializingBean接口提供的afterPropertiesSet方法

@Override
public void afterPropertiesSet() throws Exception { //在processor的before和after之间生效
    System.out.println(beanName+":afterPropertiesSet方法");
}

自定义的init方法

public void myinit() {  //需要使用init-method的标签告诉容器这是init方法
    System.out.println("自定义init");
}

使用自定义的init中的方法的时候需要在配置文件中加上init-method属性,告知容器这是这个实例的init方法

<bean id="lifeCycleBean" class="ioc1.lifeCycle.LifeCycleBean" init-method="myinit" >
    <property name="username" value="zs"/>
</bean>

注意

对于作用域是prototype的组件,每一次取出组件(getBean方法)的时候,才会开始生命周期,因为他是懒加载,这样就不用维护多余的内存空间。同时因为不是单例,那么每次加载的时候都是新的对象,也就会执行新的生命周期

容器关闭

容器在关闭的时候,会执行对应的方法,一般是为了方便资源的释放

一定要注意prototype类型的组件在容器关闭时不会执行对应的方法

DisposableBean提供的destroy方法

public interface DisposableBean {
    void destroy() throws Exception;
}

自定义的destroy方法

public void myDestroy(){
    System.out.println("自定义的destroy方法");
}

如果需要调用自定义的destroy方法,那么就需要在标签中指明该方法

<bean id="lifeCycleBean" class="ioc1.lifeCycle.LifeCycleBean" init-method="myinit" destroy-method="myDestroy">
    <property name="username" value="zs"/>
</bean>

如果自定义的destroy和DisposableBean提供的destroy方法同时都有,那么两个都会执行

这里要注意destroy方法必须要容器关闭才会执行,因此需要调用容器的close方法

applicationContext.close();

完整的生命周期的流程

1.实例化

2.参数设置–使用set方法–关联propert标签

3.如果组件实现了Aware接口,那么就会执行到Aware接口中提供的方法

4.执行BeanPostProcessor中的方法

4.1先执行beforeinit再执行afterinit

4.2如果在这中间有接口提供的或者自定义的init方法,那么就会执行

5.最后到达可用状态

**注解 **

组件注册功能的注解

在注册的时候,使用注解和全限定类名耦合起来,并通过配置文件配置一个包目录,Spring可以自动找到这个包下的所有包含注册功能注解的类

1.限定范围

2.注册功能注解

扫描包的配置文件

<!--扫描包目录:扫描的是当前包以及它的所有的子包-->
<context:component-scan base-package="com.cskaoyan"/>

常用的注册组件的注解

@Component,  通用型
@Repository,  通常是dao层的组件
@Service,  通常是service层的组件
@Controller,  SpringMVC阶段的
@RestController,  SpringMVC阶段的
@ControllerAdvice,  SpringMVC阶段的
@Configuration   Spring的配置类使用的 → JavaConfig

通过注解注册的组件,组件的id是什么?

1.默认的id值是类名的首字母小写的形式

2.自行指定:使用注解的value属性来指定

//@Component //userServiceImpl → 组件id的默认值是类名的首字母小写的形式
//@Component(value = "userService")//使用注解的value属性指定
@Component("userService")//如果注解中只有value属性,‘value=’这一部分可以省略掉
public class UserServiceImpl implements UserService{
}
作用域

@Scope 直接写在类上,在value属性中直接写作用域即可,同样的,value属性可以省略,直接写值即可

@Scope("prototype") //value属性里直接写作用域就可以了
public class UserServiceImpl implements UserService{
}
生命周期中的init和destroy方法

@PostConstruct 表示的是init方法

@PreDestroy表示的是destroy方法

public class UserServiceImpl implements UserService{

    @PostConstruct //init-method
    public void myinit() {
        System.out.println("自定义init方法");
    }
    @PreDestroy //destroy-method
    public void mydestroy() {
        System.out.println("自定义destroy方法");
    }
}

注入功能的注解

组件的注入

在组件的成员变量中引入容器中已经注册的组件,维护组件之间的依赖关系

其实就是从容器中取出组件给成员变量赋值。取出组件的方式和getBean方法是类似的

1.按照组件类型取出

2.按照组件id取出

3.按照id和类型取出

@Autowired
@Qualifier("userDaoImpl1") //按照类型和id引入
UserDao userDao1;
@Resource(name = "userDaoImpl2") // 使用@Resource注解的name属性
UserDao userDao2;
@Autowired   //按照类型
OrderDao orderDao;

常用的方法是按照类型取出

值的注入

注入组件的成员变量:接收字符串、基本类型这样的一些值

可以使用@Value注解使用SpEL,需要首先加载properties配置文件 如果要加载classpath路径,需要进行指定

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

接下来在使用的时候直接用${}的形式来引入key,就可以自动获取key中的value的值

@Value("${userservice.username}") //${}引入key → SpEL → Spring Expression Language
String username;

这里要注意写key的时候一定要增加一个自定义的前缀名,这样可以更方便的分辨属性,并且也可以避免引入系统变量

注意事项

@Autowired、@Autowired+@Qualifier、@Resource、@Value

这些注解,要在容器中的组件才能使用,因为只有组件注册了,才可以取出

单元测试

Spring单元测试:方便从容器中取出实例

直接在单元测试类使用注入功能的注解来取出组件

在单元测试类中需要维护容器 → ApplicationContext → application.xml

单元测试类中会帮我们维护好容器

引入依赖

spring-test

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.15.RELEASE</version>
    <scope>test</scope>
</dependency>

单元测试类上的注解

@Runwith(SpringJUnit4ClassRunner.class) 加载测试类的注解

@ContextConfiguration(“classpath:application.xml”) 加载配置文件的注解,一定要带上classpath

@RunWith(SpringJUnit4ClassRunner.class)//注解是junit提供的,class是spring-test
@ContextConfiguration("classpath:application.xml")
public class AnnotationTest {

    @Autowired
    OrderDao orderDao;

    @Autowired
    UserService userService;
    @Autowired
    ApplicationContext applicationContext;

    @Test
    public void mytest1() {
        userService.serviceMethod();
    }
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值