java面试之Spring(一)

1.Spring

Spring是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以控制反转(IoC)面向切面(AOP)为内核,提供了展现层 Spring MVC持久层 Spring JDBC 以及事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。是为了解决企业应用开发的复杂性而创建的。

全栈式:Spring提供了JavaEE每一层的解决方案

轻量级:Spring框架的非侵入性,即对象可以不必依赖Spring的API类

在这里插入图片描述

Data Access/Integration(数据访问/集成)
  • JDBC 模块:提供了一个 JDBC 抽象层,大幅度减少了在开发过程中对数据库操作的编码。
  • ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate和 iBatis 提供了集成层。
  • OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
  • JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
  • Transactions 事务模块:为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。
Web 模块
  • Web 模块:块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。
  • Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。pring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。
  • Websocket模块:为web应用提供的高效通信工具。
  • Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
Core Container(核心容器)
  • Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
  • Core 核心模块:提供了 Spring 框架基本组成部分,包括 IoC 和 DI 功能。
  • Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介(IOC容器)。ApplicationContext 接口是上下文模块的焦点。
  • Expression Language 模块:运行时查询和操作对象图的强大表达式语言。
其他模块
  • AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
  • Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
  • Instrumentation 模块:提供了类工具支持和类加载器的实现,可以在特定应用服务器中使用。
  • Messaging 模块:为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
  • Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架测试。

注意事项

spring-core依赖了commons-logging,而其他模块都依赖了spring-core,所以整个spring框架都依赖了commons-logging,如果有自己的日志实现如log4j,可以排除对commons-logging的依赖。

2.IOC

  • 把对象创建和对象之间的调用过程交给Spring进行管理
  • 使用IOC为了降低耦合度

IOC底层

xml解析、工厂模式、反射。通过dom4j解析xml得到类对象属性值,在通过反射机制加载类对象属性值创建对象,交由工厂封装统一对外提供。

在这里插入图片描述

在这里插入图片描述

Spring提供的IOC容器实现的两种方式

BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)

ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)

IOC操作Bean管理

Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)。

  • 普通 bean:在配置文件中定义 bean 类型就是返回类型(见以下注入)
  • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样。 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean ;第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型

Bean管理是两个操作:(1)Spring创建对象;(2)Spring注入属性(DI)

依赖注入不能单独存在,需要在ioc创建对象基础之上完成操作。

xml配置

1)普通bean

bean实例化

<bean id="springIoc" class="com.tyut.service.SpringIoc"></bean> <!--使用类的无参构造函数、2.静态工厂方法 3、实例工厂方法、实现FactoryBean接口实例化-->

bean普通属性注入

public class SpringIoc {
    //创建属性
    private String username;
    //创建属性对应的set方法
    public void setUsername(String username) {
        this.username = username;
    }
     //有参数构造
    public SpringIoc(String username) {
        this.username = username;
    }
}
<!--set方法注入-->
<bean id="springIoc" class="com.tyut.service.SpringIoc">
 <property name="username" value="liyang"></property>
</bean>
<!--有参数构造注入-->
<bean id="springIoc" class="com.tyut.service.SpringIoc">
 <constructor-arg name="username" value="liyang"></constructor-arg>
</bean>
<!--p名称空间注入,简化注入方式-->
<?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:p="http://www.springframework.org/schema/p"
       <!--在这里添加一行p-->
<bean id="springIoc" class="com.tyut.service.SpringIoc" p:username="liyang" >
</bean>

注入属性-外部bean

public class UserService {//service类
    //创建UserDao类型属性,生成set方法
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add() {
        System.out.println("service add...............");
        userDao.update();//调用dao方法
    }
}
public interface UserDao{
     public void update() {};
}
public class UserDaoImpl implements UserDao {//dao类
    @Override
    public void update() {
        System.out.println("dao update...........");
    }
}
<!--1 service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
    <!--注入userDao对象:name属性:类里面属性名称;ref属性:创建userDao对象bean标签id值-->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

注入内部bean和级联赋值

//部门类
public class Dept {
    private String dname;
    public void setDname(String dname) {
        this.dname = dname;
    }
}
//员工类
public class Emp {
    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    //方式二级联赋值:生成dept的get方法(get方法必须有!!)
    public Dept getDept() {
        return dept;
    }
}
<!--内部bean注入-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--设置两个普通属性-->
    <property name="ename" value="Andy"></property>
    <property name="gender" value=""></property>
    <!--设置对象类型属性-->
    <property name="dept">
        <bean id="dept" class="com.atguigu.spring5.bean.Dept"><!--内部bean赋值-->
            <property name="dname" value="宣传部门"></property>
        </bean>
    </property>
</bean>
<!--方式一:级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--设置两个普通属性-->
    <property name="ename" value="Andy"></property>
    <property name="gender" value=""></property>
    <!--级联赋值-->
    <property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
    <property name="dname" value="公关部门"></property>
</bean>
<!--方式二:级联赋值(生成dept的get方法)-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--设置两个普通属性-->
    <property name="ename" value="jams"></property>
    <property name="gender" value=""></property>
    <!--级联赋值-->
    <property name="dept.dname" value="技术部门"></property>
</bean>

注入集合属性

//(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
    //1 数组类型属性
    private String[] courses;
    //2 list集合类型属性
    private List<String> list;
    //3 map集合类型属性
    private Map<String,String> maps;
    //4 set集合类型属性
    private Set<String> sets;
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
}
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--set类型属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
</bean>

2)FactoryBean

//实现FactoryBean接口,重写getObject方法
public class MyBean implements FactoryBean<SpringIoc> {
    //定义返回bean
    @Override
    public SpringIoc getObject() throws Exception {
        SpringIoc springIoc = new SpringIoc();
        SpringIoc.setUsername("lymn");
        return springIoc;
    }
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean><!--bean.xml-->
@Test
public void test3() {
 ApplicationContext context =
 new ClassPathXmlApplicationContext("bean.xml");
 Course course = context.getBean("myBean", SpringIoc.class);//返回值类型可以不是定义的bean类型!
}

bean 作用域

  • singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例(默认缺省就是singleton)

  • prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例

  • 设置 scope 值是 singleton 时,加载 spring 配置文件时候就会创建单实例对象 ;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法时候创建多实例对象

  • request:每次HTTP请求将会产生不同的Bean实例

  • session:对于每次HTTP Session,使用session定义的Bean产生一个新实例

  • globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。

bean生命周期

正常生命周期为五步,而配置后置处理器后为七步。

(1)通过构造器创建 bean 实例(无参数构造)

(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)

(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

(6)bean 可以使用了(对象获取到了)

(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

public class Orders {
    //无参数构造
    public Orders() {
    System.out.println("第一步 执行无参数构造创建 bean 实例");
    }
    private String oname;
    public void setOname(String oname) {
    this.oname = oname;
    System.out.println("第二步 调用 set 方法设置属性值");
    }
    //创建执行的初始化的方法
    public void initMethod() {
    System.out.println("第三步 执行初始化的方法");
    }
    //创建执行的销毁的方法
    public void destroyMethod() {
    System.out.println("第五步 执行销毁的方法");
    }
}
//配置后置处理器后为七步
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}
@Test
 public void testBean3() {
 ClassPathXmlApplicationContext context =
 new ClassPathXmlApplicationContext("bean.xml");
 Orders orders = context.getBean("orders", Orders.class);
 System.out.println("第四步 获取创建 bean 实例对象");
 System.out.println(orders);
 //手动让 bean 实例销毁
 context.close();
 }
<!--配置文件的bean参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">	<!--配置初始化方法和销毁方法-->
    <property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>

<!--配置后置处理器,不配置为5步-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>

自动装配方式

  • no 方式:默认方式,手动装配方式,需要通过ref设定bean的依赖关系
  • byName:根据bean的名字进行装配,当一个bean的名称和其他bean的属性一致,则自动装配
  • byType:据bean的类型进行装配,当一个bean的属性类型与其他bean的属性的数据类型一致,则自动装配
  • constructor:根据构造器进行装配,与 byType 类似,如果bean的构造器有与其他bean类型相同的属性,则进行自动装配
  • autodetect:如果有默认构造器,则以constructor方式进行装配,否则以byType方式进行装配

注解

  • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
  • 使用注解,注解作用在类上面,方法上面,属性上面
  • 使用注解目的:简化 xml 配置

1)注解创建对象

下面四个注解功能是一样的,都可以用来创建 bean 实例

@Component、@Service(service层)、@Controller(web层)、@Repository(持久层)

<!--开启组件扫描 如果扫描多个包,多个包使用逗号隔开;扫描包上层目录-->
<context:component-scan base-package="com.atguigu"></context:component-scan>

<!--示例 1 use-default-filters="false" 表示现在不使用默认filter,自己配置 filtercontext:include-filter ,设置扫描哪些内容
只扫描Controller注解的类 -->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
 <context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描 Controller注解的类之外一切都进行扫描-->
<context:component-scan base-package="com.atguigu">
 <context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写 UserService -- userService
@Component(value = "userService") //注解等同于XML配置文件:<bean id="userService" class=".."/>
public class UserService {
    public void add() {
        System.out.println("service add.......");
    }
}

2)注解创建属性

  • @Autowired:根据属性类型进行自动装配
  • @Qualifier:根据名称进行注入,这个@Qualifier 注解的使用,和上面@Autowired 一起使用
  • @Resource:可以根据类型注入,也可以根据名称注入(它属于javax包下的注解,不推荐使用!)
  • @Value:注入普通类型属性
@Service
public class UserService {
 //定义 dao 类型属性 不需要添加 set 方法
 //添加注入属性注解
    @Autowired
    private UserDao userDao;
    public void add() {
        System.out.println("service add.......");
        userDao.add();
    }
}

@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add.....");
    }
}

@Autowired
//根据名称进行注入(目的在于区别同一接口下有多个实现类,根据类型就无法选择,从而出错!)
@Qualifier(value = "userDaoImpl1") 
private UserDao userDao;

//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

@Value(value = "abc")
private String name

3)完全注解开发

@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}

@Test
public void testService2() {
 //加载配置类
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService",UserService.class);
}

3.bean生命周期具体分析

Spring Bean生命周期源码分析

Spring Bean生命周期的源码分析(超级详细)

完整Spring Bean的生命周期源码解析

4.BeanFactory和FactoryBean的区别

  • BeanFactory以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
  • FactoryBean以Bean结尾,表示它是一个Bean。这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。实现了FactoryBean接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

5.@Autowired与@Resource

处理这2个注解的BeanPostProcessor不一样

CommonAnnotationBeanPostProcessor是处理@ReSource注解的

AutoWiredAnnotationBeanPostProcessor是处理@AutoWired注解的

**注入规则不一样 **

@Autowired只按照byType 注入。@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。

@Resource默认按byName自动注入,也提供按照byType 注入。如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象;当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

Spring

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值