1.0 Spring入门

文章目录

  • Spring框架概述
  • IOC的底层实现原理
  • Spring入门程序
    • Bean配置
  • IOC与DI概述
  • Spring的工厂类
  • Bean管理
    • Bean实例化
    • Bean管理常用配置
    • Bean完整的11个生命周期
    • JDK动态代理
  • Spring的属性注入
    • 构造方法注入
    • set方法注入
    • p名称空间属性注入
    • SpEL注入
  • 特殊文件的属性注入
  • 注解方式
    • Bean管理的注解
    • 属性注入的注解
  • 其他注解
    • 生命周期相关
    • Scope
    • 混合使用


Spring框架概述

  • 简化企业级应用开发

  • 一站式轻量级开源开发框架

  • 声明式事务支持,无需手动编程

  • 服务器端分为三层结构:web层(Spring提供了springMVC),业务逻辑层(Spring提供了Bean管理,即IOC,还提供了事务管理)和持久层(Spring提供JDBC模板,整合ORM框架)——每一层都提供了解决方案

  • web层需要创建业务层对象,业务层需要创建DAO层对象——》都交由Spring创建

  • 关注Spring的核心容器Core Container(IOC相关)
    1.0 Spring入门_第1张图片


IOC的底层实现原理

1.0 Spring入门_第2张图片

  • 任务:Web层需要创建业务层的类UserServiceImpl
  • 方法1:Web层直接创建接口的实现类——》Web层与业务层产生耦合(因为若要更改为另一个实现类,要修改源代码)
  • 方法2:工厂模式,但问题在于将接口和实现类的耦合变为接口与工厂类的耦合
  • 程序开发的OCP原则:程序拓展open,源码修改close
  • 基于上述分析,选用工厂+反射+配置文件,即IOC的底层原理
  • 即:解析XML文件,根据id找到全类名,然后工厂模式通过反射创建对象

  • class属性值为全类名(带包的路径)
  • 强调:IOC底层原理为通过工厂+反射+配置文件来完成解耦合

Spring入门程序

1.0 Spring入门_第3张图片

  • schema文件夹中有XML文件的约束

  • 创建项目步骤:创建接口+创建实现类
    • 选择maven项目的webapp模板
    • 自动创建完成后,pom.xml文件中添加依赖,即dependency标签,引入jar包
      (spring-core,spring-context,spring-expression,spring-beans,spring-aop,junit[4.12])
    • resource文件夹下新建spring配置文件(名字任意),创建好后会默认引入beans约束(最前面一大段)
    • 创建java文件夹,然后make source root:作为源码路径,编写代码

Bean配置

  • bean标签中id任意,class为全路径
  • Spring工厂:ApplicationContext(接口)
  • 传入XML(配置文件名)
  • getBean方法的参数为id(对应于配置文件中指定的bean),且获取返回值(Object类型)后要强转

        
    
public void demo2(){
        // 创建Spring的工厂, ApplicationContext为接口
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 通过工厂获得类:
        UserService userService = (UserService) applicationContext.getBean("userService");

        userService.sayHello();
    }

IOC与DI概述

  • IOC:控制反转,创建对象的控制权反转到spring框架
  • DI依赖于IOC,原因如下
    • 对象交给spring管理,若对象有依赖的属性——》spring要将依赖的属性注入
  • 配置属性:加入子标签property
  • 依赖注入:类依赖的属性通过配置文件设置
  • DI的原因:在传统方式中,接口没有接口实现类的属性,则不能使用多态性来改变属性,一定要采用接口实现类的引用才行——》需要修改源代码(则有耦合)——》DI能解耦合

Spring的工厂类

1.0 Spring入门_第4张图片

  • 关注层次树中三个工厂类(FileSystemXmlApplicationContext【加载磁盘系统的配置文件】与ClassPathXmlApplicationContext【加载工程下配置文件】与XmlBeanFactory)
  • 从不同路径加载配置文件,对应不同的工厂类
  • 老的工厂类BeanFactory
  • 新的工厂类(其他两个)多了国际化功能,其次,生成bean实例的时机不同,BeanFactory调用getBean方法才创建实例,而新的工厂类加载配置文件,所有单例模式【默认】配置的类都被实例化
  • 新的工厂类中的getBean方法只是获取实例

接口名 接口变量 = new 实现类名(配置文件名称);
接口变量.getBean(“id”)


Bean管理

Bean实例化

  • 实例化的三种方式(根据配置bean的方式采用对应方法)
    • 调用构造方法(默认调用无参构造器) ——最常用
    • 实例工厂:工厂类提供非静态方法,通过工厂类对象来返回bean实例
    • 静态工厂:工厂类里提供静态方法(方法名任意),返回Bean实例
 
   
    
  
    
   
    

/**
 * Bean2的静态工厂
 */
public class Bean2Factory {

    public static Bean2 createBean2(){
        System.out.println("Bean2Factory的方法已经执行了...");
        return new Bean2();
    }

}

//实例工厂
public class Bean3Factory {
    public Bean3 createBean3(){
        System.out.println("Bean3Factory执行了...");
        return new Bean3();
    }
}

  • 实例工厂的bean配置:要先有工厂的实例,才能创建对象,所以要先配置工厂Bean

Bean管理常用配置

bean标签中常用属性:

  • id和name都表示bean的别名
  • id是唯一的,name没有唯一约束
    特殊字符使用name(如含有\),id不能使用特殊字符

1.0 Spring入门_第5张图片

  • scope属性用于配置单例或多例(若为多例,则getBean每次调用相当于new新的对象),默认单例
  • 指定为request或session表示:创建类的实例,则保持在request和session作用域中

1.0 Spring入门_第6张图片

  • init-method和destory-method的方法名任意指定,只需要在配置文件中配置上
  • 销毁方法只适用于单例
  • 类被实例化则调用init-method,一般无返回值,方法名任意
  • 工厂关闭,则会销毁类的实例(单例模式前提),从而调用destroy-method方法

        
    

Bean完整的11个生命周期

1.0 Spring入门_第7张图片1.0 Spring入门_第8张图片----
展示bean实例从创建到销毁的全过程

  1. 实例化(即调用构造器)
  2. 根据property标签设置属性(要提供set方法,且配置了property子标签,name为属性名,value为属性值)
  3. 了解bean在spring中的名称,即name值(或者id值)
  4. 了解工厂信息
  5. 初始化Bean实例之前执行的方法(实现了BeanPostProcessor接口的类的一个方法) 注:实现了BeanPostProcessor接口的类不需要配置id(spring在生成bean实例后自动引用,且其他地方不使用),只需要配置class即可
  6. 属性设置后执行的方法(afterPropertiesSet())
  7. 调用Init-method指定的方法
  8. 初始化后执行的方法(实现了BeanPostProcessor接口的类的一个方法)
  9. 执行业务处理(若有调用相关方法的话)
    -----下方步骤要关闭工厂才会执行
  10. 执行DisposableBean接口的destroy方法(重写)
  11. 执行destroy-method指定的方法
  • 关注第5,8

  • 若在配置文件中配置了实现了BeanPostProcessor接口的类,则 每个Bean实例化过程中都会调用实现了BeanPostProcessor接口的类的两个方法,即所有类的实例化都会受影响

  • 基于上述分析,BeanPostProcessor接口的方法在生成某类的过程中必须执行——》则可对某类产生代理对象(在实现了BeanPostProcessor接口的类的两个方法中产生),从而增强A类的方法(见下方的权限校验)

  • 增强功能(AOP思想)

public class Man implements BeanNameAware,ApplicationContextAware,InitializingBean,DisposableBean{
    private String name;

    public void setName(String name) {
        System.out.println("第二步:设置属性");
        this.name = name;
    }

    public Man(){
        System.out.println("第一步:初始化...");
    }
    public void setup(){
        System.out.println("第七步:MAN被初始化了...");
    }

    public void teardown(){
        System.out.println("第十一步:MAN被销毁了...");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("第三步:设置Bean的名称"+name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:了解工厂信息");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:属性设置后");
    }

    public void run(){
        System.out.println("第九步:执行业务方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("第十步:执行Spring的销毁方法");
    }
}
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //System.out.println("第五步:初始化前方法...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        //System.out.println("第八步:初始化后方法...");
        if("userDao".equals(beanName)){ //若bean的id为userDao,则完成实例化后,不返回原有的bean
          //产生代理类,增强方法,最后返回代理类
            Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {//匿名内部类方式
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if("save".equals(method.getName())){//若id是userDao,且为bean中的save方法则增强该方法:先进行权限校验,再调用原有方法(方法被增强了)
                        System.out.println("权限校验===================");
                        return method.invoke(bean,args);
                    }
                    return method.invoke(bean,args);
                }
            });
            return proxy;//返回代理类
        }else{
            return bean;//id不是userDao,直接返回原有bean
        }

    }
}

JDK动态代理

见上方代码

  • 实现被代理类在类的实例化过程中,通过JDK动态代理,增强此类

Spring的属性注入

  • DI:创建类实例的同时设置实例属性

  • 注入属性的方式

  1. 构造函数注入
  2. 属性setter方法注入
  3. 接口注入
    Spring支持前两种

构造方法注入

public class User {
    private String name;
    private Integer age;

    public User(String name,Integer age){
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
   
        
        
    
  • 构造方法+constructor-arg
  • 要提供构造方法,构造器参数为对应参数
  • constructor-arg标签中还包括index,type等属性
  • index:按照下标方式进行属性注入
  • type:设置数据类型

set方法注入


   
        
        
        
    

    
        
    
  • 要提供getter/setter方法,设置property标签
  • name:属性名
  • value:普通类型
  • ref:引用类型,属性值为其他bean的id/name

p名称空间属性注入


        
    

    

    

S1:引入 p名称空间

xmlns:p=“http://www.springframework.org/schema/p”

S2:p:name="?"


SpEL注入

1.0 Spring入门_第9张图片


    
        
    

    

    
        
        
        
    
  • #{}:普通值
  • #{‘’}:字符串
  • 可使用其他类的方法或属性来设置,如 ,PS:要配置productInfo
  • 即使是引用变量,仍然使用value=?

特殊文件的属性注入

public class CollectionBean {
    private String[] arrs; // 数组类型

    private List list;// List集合类型

    private Set set; // Set集合类型

    private Map map;// Map集合类型

    private Properties properties; // 属性类型

    public String[] getArrs() {
        return arrs;
    }

    public void setArrs(String[] arrs) {
        this.arrs = arrs;
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }

    public Set getSet() {
        return set;
    }

    public void setSet(Set set) {
        this.set = set;
    }

    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }


}

    
        
        
            
                aaa
                bbb
                ccc
            
        
        
        
        
            
                111
                222
                333
            
        
        
        
        
            
                ddd
                eee
                fff
            
        

        
        
            
                
                
                
            
        
        
        
        
            
                root
                1234
            
        
    

  • 提供set方法
    成员变量为如下类型:
  • 数组类型:property——》list标签——》value/ref标签
  • list类型:property——》list标签——》value/ref标签
  • set类型:property——》set标签——》value/ref标签
  • map类型 :map标签——》entry
  • properties:pro标签

应用场景:其他框架需要复杂数据类型的注入


注解方式

使用注解方式管理bean比xml方式更方便

Bean管理的注解

1.0 Spring入门_第10张图片

  • 加入context约束

  • 开启注解扫描(扫描每个包的类中的注解,由spring管理)

  • 用注解@Component("【相当于传统方式的id】")来配置bean
  • 三种注解关键字,当前等效

属性注入的注解

  • 使用注解方式加在属性上即可,不需要setter方法,若提供了setter方法,要加在setter方法上
  • 普通类型为@value
  • 对象类型为@Resource(name=“id” ) (等价于@Autowired + @Qualifier(属性的值))
  • @Autowired+@Qualifier:强制按照名称注入(只使用@Autowired默认按照类型【成员变量的数据类型和某bean的数据类型】注入,即使名称不同也行)
  • 二合一:@Resource

其他注解

生命周期相关

  • @PostConstruct:初始化时执行,相当于init-method

  • @PreDestroy:destroy时执行,相当于destroy-method

  • 关闭工厂只能用工厂的实现类关闭,工厂接口没有此方法

Scope

@Scope:表示多例模式或单例模式

混合使用

  • XML方式管理类,注解方式用于属性注入,因为注解不需要set方法

  • 需要添加context:annotation-config/标签

  • 表示开启注解扫描,即类注入可以——》属性注入肯定也OK啊,不需要写context:annotation-config/

  • context:annotation-config/表示单独开启属性注入方式——》才可使用属性注入

你可能感兴趣的:(Spring框架学习)