spring(一)

Spring的简述

  1. Spring 是一个轻量级的框架(依赖少,消耗资源少)

  2. Spring是分层的架构,也就是y一个分层的JavaEE Full Stack(一站式)的轻量级开源框架

    也就是在经典三层(web , service , dao 层都有相应的技术解决方案)

  3. web : springMVC

    service:spring (可以做事务管理,切面编程AOP)

    dao:hibernate,mybatis,jdbcTemplate , spring-data

Spring的优点

  1. 方便解耦,简化开发

    (高内聚,低耦合)

    比如分层的时候,dao层的所有技术都放在一起,就是高内聚

    低耦合:不同的技术之间,能不调用就不调用 (比如Spring的IOC)

    让不同的层之间形成低耦合

Spring的模块

Spring的Maven依赖导入

注意:beans里面依赖包含了core,core包含了commons-logging

IOC

Inverse Of Control 控制反转

简单来说:以前是自己New一个对象实例出来,现在是由Spring容器提供一个对象实例给自己用。

配置文件:

Spring配置文件是有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">


    <!--配置Service-->
    <bean id="userService" class="com.jzp.a_ioc.UserServiceImpl"></bean>
</beans>

测试IOC

    @Test
    public void demo01(){
        //这是之前的开发
        UserServiceImpl userService = new UserServiceImpl();
        userService.addUser();
    }

    @Test
    public void demo02(){
        //spring IOC提供实例
        //1. 加载Spring配置的xml文件,获取Spring容器
        String xml = "applicationContext.xml";
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xml);
        UserService userService = (UserService) ac.getBean("userService");
        userService.addUser();
    }

问题:

Spring内部是通过什么原理实现的IOC。在xml文件中配置了全限定类名,然后在工厂里根据类名,通过反射生产出实例。

DI

Dependency Inject 依赖注入

什么是依赖,什么是注入 就是如

public class B{
    private A a; // 称B依赖于A
    public void setA(A a){
        B.a = a; //通过setter方法进行注入,称为注入
    }
}

之前的开发:

public class BookServiceImpl{
    //以前的开发   是Service与Dao耦合, 只要Dao的实现类一旦发生改变,那么这里也要改变,重新修改new的					实例名称
    //解决办法:自定义工厂,通过工厂提供
    private BookDao bookDao = new BookDaoImpl();
	
    //spring的DI解决之后(Service实现类,使用Dao接口,不知道具体的方法)
    
}

模拟Spring DI的执行过程

BookService bookService = new BookServiceImpl(); --->IoC

BookDao bookDao = new BookDaoImpl(); --->IoC

bookService.setBookDao(bookDao); 	--->DI

标签是完成IoC

标签是完成对象的属性注入DI

BeanFactory和ApplicationContext的区别

后者是前者的子接口

后者功能更完善,且不会延迟加载(也就是第一次调用getBean()时才会实例化这个Bean),

而后者是当Context(配置文件)加载完成的时候,就会立即实例化所有的Bean

测试结果证明BeanFactory的延迟加载

装配Bean基于XML

Bean实例化方式

  1. 默认构造

  2. 静态工厂 :

    Spring本身就有工厂,为什么还要静态工厂呢?

    答:静态工厂实例化Bean是用来整合其他框架常用的技术

    这就说到了单例和多例,就说到了Servlet是单例的,Structs2是多例的

    Servlet为什么是单例的: https://blog.csdn.net/wgyscsf/article/details/50129367?ref=myread

注意:在例子中,我们只是模拟了一下静态工厂是如何把实例交给Spring去管理的。

但是实际开发中,我们的工厂注入实例给Spring的应用场景是:

我们通过调用别人的工厂(在class中写需要的工厂的全限定类名+生产的实例方法) ,去注入给Spring

普通工厂

就是不用静态方法,通过生产工厂Bean,再通过工厂Bean里的工厂方法实例化对象

public class MyBeanFactory {

    //通过简单工厂(非静态工厂)创建实例

    //也就是可以创建多个工厂实例
    public  UserService createService(){
        return new UserServiceImpl();
    }
}
    <!--创建工厂Bean-->
    <bean id="MyBeanFactory" class="com.jzp.c_inject.c_factory.MyBeanFactory"></bean>

    <!--通过上面注册的工厂Bean,获取实例-->
    <bean id="userService" factory-bean="MyBeanFactory" factory-method="createService"></bean>

Bean的种类

  1. 普通Bean

  2. FactoryBean : 这是一个接口,是一个特殊的Bean,具有工厂生产对象的能力,只能生产特定的对象

    有很多实现类,此接口提供getObject()方法返回对象实例

    API文档

比如ProxyFactoryBean: 用于生产代理对象的Bean

FactoryBean和BeanFactory的区别?

BeanFactory: 是一个工厂,用来生产任意Bean

而FactoryBean:是一个特殊的Bean, 可以生产出特定的Bean出来

方法为:

Bean的作用域

        UserService userService = ac.getBean("userService", UserService.class);
        UserService userService2 = ac.getBean("userService", UserService.class);
    <!--创建实例-->
    <bean id="userService" class="com.jzp.d_scope.UserServiceImpl" scope="prototype"></bean>

Bean生命周期:

Bean的初始化与销毁
public void demo01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    String xmlPath = "applicationContext_f_lifecycle.xml";
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
    UserService userService = ac.getBean("userService", UserService.class);

    //测试销毁方法
    //通过反射获取到其子类或者父类的close()方法,并使用
    ac.getClass().getMethod("close").invoke(ac);
}
<bean id="userService" class="com.jzp.e_lifecycle.UserServiceImpl" init-method="myInit" destroy-method="myDestroy"></bean>
后处理Bean

也就是BeanPostProcessor接口,只要实现了这个接口,并注册这个Bean给Spring容器

    <!--注册后处理Bean-->
    <bean class="com.jzp.e_lifecycle.MyBeanPostProcessor"></bean>

后处理Bean的编写

public class MyBeanPostProcessor implements BeanPostProcessor {

    //导入源码后,才会人性化一点

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("前方法");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后方法");
        return bean;
    }
}

结果:

后处理Bean的原理:

A a = new A();

a = B.before(a); //用钩子B取出a ,做初始化之前的处理方法,并返回一个新的Bean对象(也可以是原来的)

a.init();

a = B.after(a); //用钩子取出a,做初始化之后的处理方法,并返回一个新的或原来的Bean

在这里返回a的JDK代理对象

为什么在后处理Bean的after()方法里返回动态代理对象呢,而不是在init()里呢?

答案:因为JDK代理对象是根据接口进行反射的,如果在init()里返回JDK代理对象,因为JDK代理对象

内部是只有业务接口方法的(而没有在UserServiceImpl实现类里的init()这些方法),所以a.init() [此处a为JDK代理对象] 永远无法执行

正确做法:在after()方法里再返回JDK代理对象

//在这里开启事务

a.addUser(); //一般我们会在这个业务方法执行之前,可以加事务控制,所以可以在此用JDK代理对象开启事务,

​ 关闭事务

目的: 能够对这个Bean的方法进行动态代理,在前后做相应的如事务处理,或者性能监控

//在这里关闭事务

a.destroy()

代理类源代码:

    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("后方法"+beanName);

        //返回JDK代理对象
        //目的: 能够对这个Bean的方法进行动态代理,在前后做相应的如事务处理,或者性能监控

        return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(method.getName().equals("addUser")){
                    System.out.println("开启事务-------");
                    //生成代理对象
                    Object object = method.invoke(bean, args);
                    System.out.println("关闭事务-------");
                    //返回代理对象
                    return object;
                }
                return null;
            }
        });
    }

问题:

getClassLoader(),顺便复习JMM内存管理, 和动态代理的具体参数的含义和内部原理

后处理Bean如何只作用于一个:

在后处理Bean实现类里的方法参数中的beanName,然后判断beanName.equals("我们想要处理的Bean名字"),即可

否则后处理Bean里的方法对所有Bean有效

动态代理的只作用于指定的method,也是同理的实现,method.getName().equals("addUser")

记住:后处理Bean必须是单例的

属性依赖注入

分为 手动装配 和 自动装配

手动装配:一般是根据XML配置文件,或者注解

  1. 基于XML装配,构造方法,setter方法
  2. 基于注解装配

自动装配: 就是根据一定的约束自动装配

  1. byType: 按类型装配
  2. byName:按名称装配
  3. constructor 构造装配
  4. autodetect: 不确定装配

构造函数注入

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

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

    public User(Integer uid, String name) {
        this.uid = uid;
        this.name = name;
    }
<!--编译器报错:没有默认的构造函数可以使用,因为我在User里写了带参构造函数-->
<!--所以要实验:构造函数的注入-->
<bean id="user" class="com.jzp.f_xml.a.constructor.User">
    <constructor-arg name="name" value="zhanp"></constructor-arg>
    <constructor-arg name="age" value="20"></constructor-arg>
</bean>

第二种:

    <bean id="user" class="com.jzp.f_xml.a.constructor.User">
        <!--index为索引,type为当多个构造方法匹配时,如何区分注入的属性类型-->
        <constructor-arg index="0" type="java.lang.String" value="zhanp"></constructor-arg>
        <constructor-arg index="1" type="java.lang.Integer" value="20"></constructor-arg>
    </bean>

装配Bean 基于注解

​ 注解:就是一个类,使用@注解名称
​ 开发中:使用注解 取代 xml配置文件。

  1. @Component取代
    @Component("id") 取代
    2.web开发,提供3个@Component注解衍生注解(功能一样)取代
    @Repository :dao层
    @Service:service层
    @Controller:web层
    3.依赖注入 ,给私有字段设置,也可以给setter方法设置
    普通值:@Value("")
    引用值:
    ​ 方式1:按照【类型】注入
    ​ @Autowired
    ​ 方式2:按照【名称】注入1
    ​ @Autowired
    ​ @Qualifier("名称")
    ​ 方式3:按照【名称】注入2
    ​ @Resource("名称")
    4.生命周期
    初始化:@PostConstruct
    销毁:@PreDestroy
    5.作用域
    @Scope("prototype") 多例

    注解使用前提,添加命名空间,让spring扫描含有注解类

<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 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/context 
       					   http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 组件扫描,扫描含有注解的类 -->
	<context:component-scan base-package="com.itheima.g_annotation.a_ioc"> </context:component-scan>
</beans>
原文地址:https://www.cnblogs.com/zhanp/p/10931931.html